After running the demultiplexer_for_dada2 (http://github.com/ramongallego/demultiplexer_for_dada2), we have to denoise the whole dataset. We will do this by using 4 different processes:

As with everything, we will start the process by loading the required packages and datasets.

Load the dataset and metadata

We will load the ASV table and the metadata file. They are in the same folder so we use list.files to access them and a neat combination of bind.rows and map(read_csv).

Hash.key <- bind_rows(map(all.hashes, read_csv))
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)

Data Cleanup - Don’t act like you don’t need this

A few things we check for: That no sample appears twice in the metadata. That the metadata uses Tag_01 instead of Tag_1 (so it can be sorted alphabetically). That the structure Site_YYYYMM[A-C].[1-3] is the same across the dataset.


# Check that no sample appears more than once in the metadata

metadata %>% 
  group_by(sample_id) %>%
  summarise(tot = n()) %>% 
  arrange(desc(tot)) # Samples only appear once

# We should change Tag_1 for Tag_01

metadata %>%
  mutate(Tag = case_when(str_detect(Tag, "\\_[0-9]{1}$")       ~     str_replace(Tag, "Tag_", "Tag_0"),
                         TRUE                                  ~     Tag  )) -> metadata



metadata %>% mutate(x= str_count(sample_id, pattern = "_")) %>% arrange(desc(x)) # Lilliwaup 201710 has 2 underscores, run 3 metadata has no underscore

   
ASV.table %>% distinct(sample) %>% mutate(x= str_count(sample, pattern = "_")) %>% arrange(desc(x)) # Also in the ASV table

# Fix it here

metadata %>% mutate(x= str_count(sample_id, pattern = "_")) %>%
  mutate(sample_id = case_when(x == 2 ~ str_replace(sample_id, pattern = "_B", replacement = "B"),
                               TRUE   ~ sample_id)) %>% 
  dplyr::select(-x) -> metadata

ASV.table %>% mutate(x= str_count(sample, pattern = "_")) %>%
  mutate(sample = case_when(x == 2 ~ str_replace(sample, pattern = "_B", replacement = "B"),
                               TRUE   ~ sample)) %>% 
  dplyr::select(-x) -> ASV.table 

# Done

# Change Kangaroo.4 for Kangaroo.3. Also Change Ostrich1 Ostrich2 and Ostrich3 for .1, .2 .3

metadata %>% 
  mutate (sample_id = case_when (str_detect(sample_id, "Ostrich[123]") ~ str_replace(sample_id, "Ostrich", "Ostrich\\."),
                                 sample_id == "Kangaroo.4"             ~ "Kangaroo.3",
                                 TRUE                                  ~ sample_id)) -> metadata
ASV.table %>%
  mutate (sample = case_when (str_detect(sample, "Ostrich[123]") ~ str_replace(sample, "Ostrich", "Ostrich\\."),
                                 sample == "Kangaroo.4"          ~ "Kangaroo.3",
                                 TRUE                            ~ sample)) -> ASV.table

# DONE

# Add underscore to run3

ASV.table %>% 
  filter(str_detect(sample, "[A-Z][A-Z]2017")) %>% 
  distinct(Miseq_run) # it only affects one run

ASV.table %>% 
  mutate (sample = case_when(str_detect(sample, "[A-Z][A-Z]2017")    ~    str_replace(sample,"2017", "_2017" ),
                             TRUE                                    ~    sample)) -> ASV.table

# ASV table from Run 1 has the full date instead of just YYYYMM

ASV.table %>% 
  filter(str_detect(sample, "_[:digit:]{8}")) # YES

ASV.table %>% 
  mutate(sample = case_when(str_detect(sample, "_[:digit:]{8}")    ~  paste0(str_replace(sample, "_[:digit:]{8}", str_extract(sample,"_[:digit:]{6}" ))),
                            TRUE                                   ~  sample)) -> ASV.table

# in MiSeqRun1, there are duplicated entries of some Hash / sample combos, probably bc we combined the Miseq and MiSeq nano runs


ASV.table %>% 
  group_by(Miseq_run, sample, Hash) %>% 
  summarise(nReads = sum(nReads)) %>% 
  ungroup -> ASV.table

The output of this process are a clean ASV table and a clean metadata file.

Cleaning Process 1: Estimation of Tag-jumping or sample cross-talk

Before we modify our datasets on any way, we can calculate how many sequences that were only supposed to be in the positives control appeared in the environmental samples, and how many did the opposite. First we divide the dataset into positive control and environmental samples. Also create an ordered list of the Hashes present in the positive controls, for ease of plotting


ASV.table %>%  mutate(source = case_when(str_detect(sample, "Kangaroo|K\\+|k\\+|Ostrich")    ~   "Positives",
                                         TRUE                                                ~   "Samples")) -> ASV.table

ASV.table %>% 
  filter (source == "Positives") %>% 
  group_by(Hash) %>% 
  summarise(tot = sum(nReads)) %>% 
  arrange(desc(tot)) %>% 
  pull(Hash) -> good.order

Now let’s create a jumping vector. What proportion of the reads found in the positives control come from elsewhere, and what proportion of the reads in the samples come from the positives control.

Step 1: Nest the dataset and split it in positives and samples

To streamline the process and make it easier to execute it similarly but independently on each Miseq run, we nest the dataset by run. So Step1 is create a nested table so we can run this analysis on each run independently.

That wasn’t too complicated. Let’s start a summary function that keeps track of our cleaning process

Step 2: Model the composition of the positive controls of each run

We create a vector of the composition of each positive control and substract it from the environmental samples from their runs

Step 3: Substract the composition of the positive controls from the environment samples

The idea behind this procedure is that we know, for each run, how many reads from each Hash appeared in teh positive controls. These come from 2 processes: sequences we know should appear in the positive controls, and sequences that have jumped from the environment to the positive controls. With this procedure, we substract from every environmental sample the proportion of reads that jumped from elsewhere.

ASV.nested %>% 
  mutate(cleaned.tibble = map2(Samples, contam.tibble, function(.x,.y){ 
    .x %>%
      group_by (sample) %>%
      mutate (TotalReadsperSample = sum (nReads)) %>%
      left_join(.y, by = "Hash") %>%
      mutate (Updated_nReads = ifelse (!is.na(vector_contamination),  nReads - (ceiling(vector_contamination*TotalReadsperSample)), nReads)) %>%
      filter (Updated_nReads > 0) %>%
      ungroup() %>% 
      dplyr::select (sample, Hash, nReads = Updated_nReads)
      
    
  })) -> ASV.nested

ASV.nested %>% 
  group_by(Miseq_run) %>% 
  select(cleaned.tibble) %>% 
  unnest(cleaned.tibble) #Check how they look
Adding missing grouping variables: `Miseq_run`

Add this step to the summary table we were creating

ASV.nested %>% 
  transmute(Miseq_run, Summary.1 = map(cleaned.tibble, ~ how.many(ASVtable = .,round = "1.Jump"))) %>% 
  left_join(ASV.summary) %>% 
  mutate(Summary   = map2(Summary, Summary.1, bind_rows)) %>%
  dplyr::select(-Summary.1) -> ASV.summary 
Joining, by = "Miseq_run"

Cleaning Process 2: Discarding PCR replicates with low number of reads

We will fit the number of reads assigned to each sample to a normal distribution and discard those samples with a probability of 95% of not fitting in that distribution. The output would be a dataset with less samples and potentially less number of unique Hashes.

ASV.nested %>% 
  transmute(Miseq_run, Summary.1 = map(Step.1.low.reads, ~ how.many(ASVtable = .,round = "2.Low.nReads"))) %>% 
  left_join(ASV.summary) %>% 
  mutate(Summary   = map2(Summary, Summary.1, bind_rows)) %>%
  dplyr::select(-Summary.1) -> ASV.summary 
Joining, by = "Miseq_run"

Cleaning Process 3: Full clearance from Positive control influence

Removing the Hashes that belong to the positive controls. First, for each Hash that appeared in the positive controls, determine whether a sequence is a true positive or a true environment. For each Hash, we will calculate, maximum, mean and total number of reads in both positive and samples, and then we will use the following decission tree:

  • If all three statistics are higher in one of the groups, we will label it either of Environmental or Positive control influence.

  • If there are conflicting results, we will use the Hashes. to see if they belong to either the maximum abundance of a Hash is in a positive, then it is a positive, otherwise is a real sequence from the environment.

Now, for each Hash in each set of positives controls, calculate the proportion of reads that were missasigned - they appeared somewhere they were not expected. We will divide that process in two: first . A second step would be to create a column named proportion switched, which states the proportion of reads from one Hash that jumped from the environment to a positive control or viceversa. The idea is that any presence below a threshold can be arguably belong to tag jumping.

ASV.table %>% 
  group_by(source, Hash) %>% 
  summarise(ocurrences =n()) %>% 
  spread(key = source, value = ocurrences, fill = 0) %>% 
  #left_join(Hashes.to.remove.step2) %>% 
  #mutate(origin = case_when(is.na(origin) ~ "Kept",
   #                         TRUE          ~ "Discarded")) %>% 
  mutate(second.origin = case_when(Positives >= Samples ~ "Discarded",
                                   TRUE                 ~ "Kept")) %>% 
  filter(second.origin == "Discarded") %>% 
  full_join(Hashes.to.remove.step2) -> Hashes.to.remove.step2
Joining, by = "Hash"

IN order to train DADA2 to better distinguish when positive control sequences have arrived in the environment, we will keep the sequences in a csv file

Hashes.to.remove.step2 %>% 
  left_join(Hash.key) %>% 
  select(Hash, Sequence) %>% 
  write_csv("Hashes.to.remove.csv")
Joining, by = "Hash"

Remove the positive control hashes from the composition of the ASVs

ASV.summary %>% 
  unnest()
`cols` is now required.
Please use `cols = c(Summary)`

Cleaning Process 4: Occupancy modelling

What is the probabilty of a true positive presence of a Hash in a Miseq Run. We will use eDNA occupancy modeling to asses whether a hash is a rare variant that spilled out or a true presence.

The process requires to load extra packages, create some model file, and group the hashes by Run, and biological replicate, summarising the data in a presence absence format.

The occupancy model itself was performed in the Rmarkdown file Rjags.tunning.Rmd, so here we will upload the csv file that contains all probability of occurences of all hashes per site. Each Hash-Site combination produces a matrix of presence abascences that feeds the model - for some cases it is a 30x3 matrix, for others it is a 39x3. We summarised the number of occurences in each case and run models for each unique case (to save computing time). Each unique model was run 10 times to filter out cases in which the model converge into a local maxima.

So we will import the object Occ.fate.csv and reduce the dataset to those Hashes with an occ > 0.8

So we will throw away most of the Hashes, but will keep most of the reads - we are getting into something here

 occ.results %>% 
  filter(real > 0.8) %>% 
  pull (Hash) -> to.keep

ASV.nested %>% 
  mutate(Step3.tibble = map (Step2.tibble, ~ filter(.,Hash %in% to.keep))) -> ASV.nested

ASV.nested %>% 
  transmute(Miseq_run, Summary.1 = map(Step3.tibble, ~ how.many(ASVtable = .,round = "4.Occupancy"))) %>% 
  left_join(ASV.summary) %>% 
  mutate(Summary   = map2(Summary, Summary.1, bind_rows)) %>%
  dplyr::select(-Summary.1) -> ASV.summary 
Joining, by = "Miseq_run"

Cleaning Process 5: Dissimilarity between PCR replicates

So, a second way of cleaning the dataset is to remove samples for which the dissimilarity between PCR replicates exceeds the normal distribution of dissimilarities. Sometimes the preparation of a PCR replicate goes wrong for a number of reasons - that leads to a particular PCR replicate to be substantially different to the other 2. In that case, we will remove the PCR replicate that has higher dissimilarity with the other two.

The process starts by adding the biological information to the ASV table, then diving the dataset by their biological replicate. This will also remove any sample that is not included in the metadata, eg coming from a different project.

ASV.nested %>% 
  select(Miseq_run, Step3.tibble) %>% 
  unnest(Step3.tibble) %>%
  separate(sample, into = "original_sample", sep = "\\.", remove = F) -> cleaned.tibble
Expected 1 pieces. Additional pieces discarded in 102752 rows [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].

Ok, so there are 13 samples for which we only have 2 PCR replicates1. We will get rid of those with only 1, as we can’t estimate the PCR bias there.

Anyway, let’s have a visual representation of the dissimilarities between PCR replicates, biological replicates and everything else.

cleaned.tibble %>%
  group_by (sample) %>%
  mutate (Tot = sum(nReads),
          Row.sums = nReads / Tot) %>% 
  group_by (Hash) %>%
  mutate (Colmax = max (Row.sums),
          Normalized.reads = Row.sums / Colmax) -> cleaned.tibble
tibble_to_matrix <- function (tb) {
  
  tb %>% 
    group_by(sample, Hash) %>% 
    summarise(nReads = sum(Normalized.reads)) %>% 
    spread ( key = "Hash", value = "nReads", fill = 0) -> matrix_1
    samples <- pull (matrix_1, sample)
    matrix_1 %>% 
      ungroup() %>% 
    dplyr::select ( - sample) -> matrix_1
    data.matrix(matrix_1) -> matrix_1
    dimnames(matrix_1)[[1]] <- samples
    vegdist(matrix_1) -> matrix_1
}

tibble_to_matrix (cleaned.tibble) -> all.distances.full

#names(all.distances.full)


summary(is.na(names(all.distances.full)))
   Mode   FALSE 
logical     770 

Let’s make the pairwaise distances a long table


as.tibble(subset(melt(as.matrix(all.distances.full)))) -> all.distances.melted
`as.tibble()` is deprecated, use `as_tibble()` (but mind the new semantics).
This warning is displayed once per session.
summary(is.na(all.distances.melted$value))
   Mode   FALSE 
logical  592900 
# Now, create a three variables for all distances, they could be PCR replicates, BIOL replicates, or from the same site

all.distances.melted %>%
  separate (Var1, into = "Bottle1", sep = "\\.", remove = FALSE) %>%
  separate (Bottle1, into = "Site1", remove = FALSE) %>%
  separate (Var2, into ="Bottle2", sep = "\\.", remove = FALSE) %>%
  separate (Bottle2, into = "Site2", remove = FALSE) %>%
  mutate ( Day.site1 = str_sub(Bottle1, start = 1, end = -2),
           Day.site2 = str_sub(Bottle2, start = 1, end = -2),
           Distance.type = case_when( Bottle1 == Bottle2 ~ "PCR.replicates",
                                      Day.site1 == Day.site2 ~ "Biol.replicates",
                                      Site1 == Site2 ~ "Same Site",
                                      TRUE ~ "Different Site"
                                     )) %>%
  dplyr::select(Sample1 = Var1, Sample2 = Var2 , value , Distance.type) %>%
  filter (Sample1 != Sample2) -> all.distances.to.plot
Expected 1 pieces. Additional pieces discarded in 592900 rows [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].Expected 1 pieces. Additional pieces discarded in 592900 rows [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].Expected 1 pieces. Additional pieces discarded in 592900 rows [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].Expected 1 pieces. Additional pieces discarded in 592900 rows [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].
# Checking all went well

sapply(all.distances.to.plot, function(x) summary(is.na(x)))
      Sample1   Sample2   value     Distance.type
Mode  "logical" "logical" "logical" "logical"    
FALSE "592130"  "592130"  "592130"  "592130"     
all.distances.to.plot$Distance.type <- all.distances.to.plot$Distance.type  %>% fct_relevel( "PCR.replicates", "Biol.replicates", "Same Site")

  ggplot (all.distances.to.plot , aes (fill = Distance.type, x = value)) +
  geom_histogram (position = "dodge", stat = 'density', alpha = 0.9) +
 # facet_wrap( ~ Distance.type) +
  labs (x = "Pairwise dissimilarity", y = "density" ,
        Distance.type = "Distance")
Ignoring unknown parameters: binwidth, bins, pad

NA
# Instead of chosing based on the pw distances, we can do a similar thing using the distance to centroid

# Find out which samples have only two pcr replicates
cleaned.tibble %>% dplyr::select(-Miseq_run) %>% group_by(original_sample) %>% nest() -> nested.cleaning

nested.cleaning %>% 
  mutate(matrix = map(data, tibble_to_matrix)) -> nested.cleaning
nested.cleaning %>% mutate(ncomparisons = map(matrix, length)) -> nested.cleaning
 
  
dist_to_centroid <- function (x,y) {
  biol <- rep(y, length(x))
  
  if (length(biol) == 1) {
    output = rep(x[1]/2,2)
    names(output) <- attr(x, "Labels")
  }else{ 
    
  dispersion <- betadisper(x, group = biol)
  output = dispersion$distances
  }
  output
    }


nested.cleaning <- nested.cleaning %>% mutate (distances = map2(matrix, original_sample, dist_to_centroid))

unlist (nested.cleaning$distances) -> all_distances
#normparams <- fitdistr(all_pairwise_distances, "normal")$estimate
normparams <- MASS::fitdistr(all_distances, "normal")$estimate
#  probs <- pnorm(all_pairwise_distances, normparams[1], normparams[2])
probs <- pnorm(all_distances, normparams[1], normparams[2])
outliers <- which(probs>0.95)

discard <-names (all_distances[outliers])

all_distances
LL_201703A.1 LL_201703A.2 LL_201703A.3 LL_201703B.1 LL_201703B.2 LL_201703B.3 LL_201703D.1 LL_201703D.2 LL_201703D.3 
  0.13625301   0.35880707   0.24481834   0.11786624   0.17375479   0.15871014   0.25237790   0.17664282   0.13972079 
LL_201703E.1 LL_201703E.2 LL_201703E.3 LL_201703F.1 LL_201703F.2 LL_201703F.3 PO_201703A.1 PO_201703A.2 PO_201703A.3 
  0.18874104   0.08762146   0.12654161   0.28378689   0.17883503   0.07654715   0.16471911   0.33485159   0.07873209 
PO_201703B.1 PO_201703B.2 PO_201703B.3 PO_201703C.1 PO_201703C.2 PO_201703C.3 PO_201703D.1 PO_201703D.2 PO_201703D.3 
  0.12452701   0.38124568   0.19686350   0.16156567   0.25456026   0.13474249   0.23816473   0.34690721   0.13841017 
PO_201703E.1 PO_201703E.2 PO_201703E.3 PO_201703F.1 PO_201703F.2 PO_201703F.3 TW_201703A.1 TW_201703A.2 TW_201703A.3 
  0.13773561   0.30286292   0.08269833   0.13585790   0.33762093   0.16755540   0.03097821   0.41234310   0.24047281 
TW_201703B.1 TW_201703B.2 TW_201703B.3 TW_201703C.1 TW_201703C.2 TW_201703C.3 TW_201703D.2 TW_201703D.3 TW_201703E.1 
  0.19596800   0.36860454   0.06147847   0.24031588   0.31569610   0.22246450   0.11192855   0.11192855   0.53705208 
TW_201703E.2 TW_201703E.3 TW_201703F.1 TW_201703F.2 TW_201703F.3 LL_201707A.1 LL_201707A.2 LL_201707A.3 LL_201707B.1 
  0.13460879   0.13150055   0.09580705   0.32591372   0.24652571   0.19565941   0.40786283   0.12533363   0.19577566 
LL_201707B.2 LL_201707B.3 LL_201707C.1 LL_201707C.2 LL_201707C.3 LL_201708A.1 LL_201708A.2 LL_201708A.3 LL_201708B.1 
  0.15587693   0.16513749   0.12837155   0.09044335   0.20694010   0.25080218   0.31451228   0.29089518   0.28849028 
LL_201708B.2 LL_201708B.3 LL_201708C.1 LL_201708C.2 LL_201708C.3 PO_201707A.1 PO_201707A.2 PO_201707A.3 PO_201707B.1 
  0.27307191   0.30102499   0.17368227   0.23292150   0.16953950   0.16073332   0.31438009   0.25899910   0.25069954 
PO_201707B.2 PO_201707B.3 PO_201707C.1 PO_201707C.2 PO_201707C.3 PO_201708A.1 PO_201708A.2 PO_201708A.3 PO_201708B.1 
  0.26790802   0.25799422   0.31519903   0.18404621   0.28670614   0.22407936   0.19145599   0.31770854   0.37691742 
PO_201708B.2 PO_201708B.3 PO_201708C.1 PO_201708C.2 PO_201708C.3 SA_201707A.1 SA_201707A.2 SA_201707A.3 SA_201707B.1 
  0.32397896   0.31838533   0.20371194   0.29164108   0.22670963   0.26677486   0.16661059   0.12543549   0.28282472 
SA_201707B.2 SA_201707B.3 SA_201707C.1 SA_201707C.2 SA_201707C.3 SA_201708A.1 SA_201708A.2 SA_201708A.3 SA_201708B.1 
  0.21057414   0.21492075   0.17424903   0.26579481   0.16401597   0.19144035   0.17536249   0.19687628   0.23920250 
SA_201708B.2 SA_201708B.3 SA_201708C.1 SA_201708C.2 SA_201708C.3 TR_201707A.1 TR_201707A.2 TR_201707A.3 TR_201707B.1 
  0.28041200   0.20222634   0.16198006   0.18458623   0.16482170   0.38078410   0.24574496   0.18691928   0.23480187 
TR_201707B.2 TR_201707B.3 TR_201707C.1 TR_201707C.2 TR_201707C.3 TR_201708A.1 TR_201708A.2 TR_201708A.3 TR_201708B.1 
  0.13953084   0.11437770   0.29745958   0.27987091   0.13166308   0.23142577   0.21463294   0.19578625   0.13510875 
TR_201708B.2 TR_201708B.3 TR_201708C.1 TR_201708C.2 TR_201708C.3 TW_201707A.1 TW_201707A.2 TW_201707A.3 TW_201707B.1 
  0.36128237   0.19323695   0.11327835   0.12523905   0.13876746   0.09997909   0.18137468   0.13400328   0.09596006 
TW_201707B.2 TW_201707B.3 TW_201707C.1 TW_201707C.2 TW_201707C.3 TW_201708A.1 TW_201708A.2 TW_201708A.3 TW_201708B.1 
  0.27326407   0.12207356   0.15080762   0.09547681   0.11968098   0.28083478   0.32789627   0.29770788   0.28716231 
TW_201708B.2 TW_201708B.3 TW_201708C.1 TW_201708C.2 TW_201708C.3 LL_201705A.1 LL_201705A.2 LL_201705A.3 LL_201705B.1 
  0.31331817   0.17634853   0.18956755   0.32541748   0.24027209   0.12954176   0.48811052   0.42631204   0.30492711 
LL_201705B.2 LL_201705B.3 LL_201705C.1 LL_201705C.2 LL_201705C.3 LL_201706A.1 LL_201706A.2 LL_201706A.3 LL_201706B.1 
  0.34941102   0.41831325   0.18253668   0.31187022   0.18513841   0.56530804   0.36202416   0.34475521   0.35531412 
LL_201706B.2 LL_201706B.3 LL_201706C.1 LL_201706C.2 LL_201706C.3 PO_201705A.1 PO_201705A.2 PO_201705A.3 PO_201705B.1 
  0.37909891   0.37585944   0.48446426   0.38214867   0.37377514   0.08007319   0.43221260   0.24163768   0.19559611 
PO_201705B.2 PO_201705B.3 PO_201705C.1 PO_201705C.2 PO_201705C.3 PO_201706A.1 PO_201706A.2 PO_201706A.3 PO_201706B.1 
  0.28648086   0.46859631   0.08816983   0.07915061   0.06754419   0.37558200   0.40250367   0.48139216   0.33057062 
PO_201706B.2 PO_201706B.3 PO_201706C.1 PO_201706C.2 PO_201706C.3 SA_201705A.1 SA_201705A.2 SA_201705A.3 SA_201705B.1 
  0.46966150   0.45009175   0.23509942   0.28978770   0.34004489   0.30394183   0.29724296   0.31578554   0.28114732 
SA_201705B.2 SA_201705B.3 SA_201705C.1 SA_201705C.2 SA_201705C.3 SA_201706A.1 SA_201706A.2 SA_201706A.3 SA_201706B.1 
  0.20616198   0.26757541   0.36679177   0.33242289   0.43883854   0.26551454   0.34030673   0.20479601   0.21376698 
SA_201706B.2 SA_201706B.3 SA_201706C.1 SA_201706C.2 SA_201706C.3 TR_201705A.1 TR_201705A.2 TR_201705A.3 TR_201705B.1 
  0.24904907   0.25091453   0.43107378   0.35012786   0.40933099   0.18653370   0.21615233   0.32541795   0.30795000 
TR_201705B.2 TR_201705B.3 TR_201705C.1 TR_201705C.2 TR_201705C.3 TR_201706A.1 TR_201706A.2 TR_201706A.3 TR_201706B.1 
  0.21313227   0.20223004   0.22978337   0.26355714   0.26880833   0.47653719   0.36284917   0.44234845   0.42696790 
TR_201706B.2 TR_201706B.3 TR_201706C.1 TR_201706C.2 TR_201706C.3 TW_201705A.1 TW_201705A.2 TW_201705A.3 TW_201705B.1 
  0.37846865   0.42697207   0.37347632   0.41453446   0.45008975   0.34701836   0.39979918   0.38399138   0.41793660 
TW_201705B.2 TW_201705B.3 TW_201705C.1 TW_201705C.2 TW_201705C.3 TW_201706A.1 TW_201706A.2 TW_201706B.1 TW_201706B.2 
  0.21959514   0.49431996   0.19049863   0.40741979   0.29835490   0.19308072   0.19308072   0.21292407   0.26251765 
TW_201706B.3 TW_201706C.1 TW_201706C.2 TW_201706C.3 CP_201709A.1 CP_201709A.2 CP_201709A.3 CP_201709B.1 CP_201709B.2 
  0.34853135   0.42927899   0.37043719   0.54961074   0.28346536   0.26419108   0.38925021   0.31723262   0.32281930 
CP_201709B.3 CP_201709C.1 CP_201709C.3 CP_201710A.1 CP_201710A.2 CP_201710A.3 CP_201710B.1 CP_201710B.2 CP_201710B.3 
  0.36571132   0.23905735   0.23905735   0.32726693   0.26619898   0.43612053   0.35641148   0.31747358   0.47663887 
CP_201710C.1 CP_201710C.2 CP_201710C.3 CP_201711A.1 CP_201711A.2 CP_201711A.3 CP_201711B.1 CP_201711B.2 CP_201711B.3 
  0.47434020   0.40350614   0.22359320   0.51470154   0.34584551   0.34546043   0.33377101   0.36916055   0.39501211 
CP_201711C.1 CP_201711C.2 CP_201711C.3 FH_201709A.1 FH_201709A.2 FH_201709A.3 FH_201709B.1 FH_201709B.2 FH_201709B.3 
  0.40595165   0.33673637   0.48307825   0.33644332   0.35209724   0.26551978   0.41035606   0.30702749   0.32067942 
FH_201709C.1 FH_201709C.2 FH_201709C.3 FH_201710A.1 FH_201710A.2 FH_201710A.3 FH_201710B.1 FH_201710B.2 FH_201710B.3 
  0.26647980   0.19417930   0.76447430   0.26813546   0.29281847   0.32829062   0.27233127   0.37126246   0.41221457 
FH_201710C.1 FH_201710C.2 FH_201710C.3 FH_201711A.1 FH_201711A.2 FH_201711A.3 FH_201711B.1 FH_201711B.2 FH_201711B.3 
  0.17129342   0.20285746   0.29600655   0.34155977   0.33063567   0.30661559   0.43475539   0.24497509   0.36555637 
FH_201711C.1 FH_201711C.2 FH_201711C.3 LK_201709A.1 LK_201709A.2 LK_201709A.3 LK_201709B.1 LK_201709B.2 LK_201709B.3 
  0.33712471   0.35624770   0.23467041   0.35645449   0.35413460   0.33921717   0.32551911   0.34602147   0.52233141 
LK_201709C.1 LK_201709C.2 LK_201709C.3 LK_201710A.2 LK_201710A.3 LK_201710B.1 LK_201710B.2 LK_201710B.3 LK_201710C.1 
  0.35532169   0.34811992   0.35041892   0.27608950   0.27608950   0.22730033   0.32654848   0.43138500   0.27753417 
LK_201710C.2 LK_201710C.3 LK_201711A.2 LK_201711A.3 LK_201711B.1 LK_201711B.2 LK_201711B.3 LK_201711C.1 LK_201711C.2 
  0.29766125   0.36382022   0.24666728   0.24666728   0.46879536   0.45024465   0.45525376   0.28981385   0.43091660 
LK_201711C.3 CP_201706A.1 CP_201706A.2 CP_201706A.3 CP_201706B.1 CP_201706B.2 CP_201706B.3 CP_201706C.1 CP_201706C.2 
  0.32064041   0.47394873   0.46482096   0.51916585   0.39031387   0.37969046   0.42702409   0.39836255   0.45349341 
CP_201706C.3 CP_201707A.1 CP_201707A.2 CP_201707A.3 CP_201707B.1 CP_201707B.2 CP_201707B.3 CP_201707C.1 CP_201707C.2 
  0.51752685   0.31394900   0.22743413   0.44972476   0.45832210   0.39398824   0.46587899   0.37458273   0.45897945 
CP_201707C.3 CP_201708A.1 CP_201708A.2 CP_201708A.3 CP_201708B.1 CP_201708B.2 CP_201708B.3 CP_201708C.1 CP_201708C.2 
  0.52780300   0.30225251   0.38002402   0.28869795   0.47252551   0.24970573   0.22972008   0.16113411   0.21307242 
CP_201708C.3 FH_201706A.1 FH_201706A.2 FH_201706A.3 FH_201706B.1 FH_201706B.2 FH_201706B.3 FH_201706C.1 FH_201706C.2 
  0.33747259   0.46184806   0.19971283   0.39732658   0.25924218   0.27109590   0.41901540   0.44738877   0.20884756 
FH_201706C.3 FH_201707A.1 FH_201707A.2 FH_201707A.3 FH_201707B.1 FH_201707B.2 FH_201707B.3 FH_201707C.1 FH_201707C.2 
  0.42540736   0.35776616   0.30399299   0.32354568   0.23142426   0.32471335   0.38370174   0.39951644   0.21756462 
FH_201707C.3 FH_201708A.1 FH_201708A.2 FH_201708A.3 FH_201708B.1 FH_201708B.2 FH_201708B.3 FH_201708C.1 FH_201708C.2 
  0.30328849   0.37888811   0.37000684   0.39572162   0.33982587   0.41549391   0.42939253   0.38569780   0.49341359 
FH_201708C.3 LK_201706A.1 LK_201706A.2 LK_201706A.3 LK_201706B.1 LK_201706B.2 LK_201706B.3 LK_201706C.1 LK_201706C.2 
  0.33215175   0.48636473   0.30835058   0.44539395   0.48361955   0.46553149   0.50700960   0.35910315   0.42315450 
LK_201706C.3 LK_201707A.1 LK_201707A.2 LK_201707A.3 LK_201707B.1 LK_201707B.2 LK_201707B.3 LK_201707C.1 LK_201707C.2 
  0.51145593   0.47484641   0.36616877   0.41655854   0.36136712   0.32792126   0.36431089   0.34199257   0.31924573 
LK_201707C.3 LK_201708A.1 LK_201708A.2 LK_201708A.3 LK_201708B.1 LK_201708B.2 LK_201708B.3 LK_201708C.1 LK_201708C.2 
  0.46478331   0.43810258   0.48133497   0.55217919   0.29875879   0.32826587   0.50391979   0.28446651   0.28131990 
LK_201708C.3 LL_201709A.1 LL_201709A.2 LL_201709A.3 LL_201709B.1 LL_201709B.2 LL_201709B.3 LL_201709C.1 LL_201709C.2 
  0.32600456   0.40867064   0.34286639   0.29105243   0.38507559   0.40716639   0.28675735   0.39925843   0.38629920 
LL_201709C.3 LL_201711B.1 LL_201711B.2 LL_201711B.3 LL_201711C.1 LL_201711C.2 LL_201711C.3 PO_201709A.1 PO_201709A.2 
  0.32997238   0.35007347   0.36504310   0.53671177   0.32809664   0.57652762   0.38745339   0.43647117   0.31838672 
PO_201709A.3 PO_201709B.1 PO_201709B.2 PO_201709B.3 PO_201709C.1 PO_201709C.2 PO_201709C.3 PO_201711A.1 PO_201711A.2 
  0.34159786   0.32463241   0.32705121   0.35063631   0.38354254   0.41703252   0.36869710   0.23649847   0.31997618 
PO_201711A.3 PO_201711B.1 PO_201711B.2 PO_201711B.3 PO_201711C.1 PO_201711C.2 PO_201711C.3 SA_201703A.1 SA_201703A.2 
  0.35557277   0.28930790   0.56591479   0.27468177   0.34120491   0.40152095   0.36530179   0.22415599   0.37086495 
SA_201703A.3 SA_201703B.1 SA_201703B.2 SA_201703B.3 SA_201703C.1 SA_201703C.2 SA_201703C.3 SA_201709A.1 SA_201709A.2 
  0.30826070   0.27558471   0.28427322   0.32591160   0.23167323   0.27031755   0.28827965   0.34400676   0.78225573 
SA_201709A.3 SA_201709B.1 SA_201709B.2 SA_201709B.3 SA_201709C.1 SA_201709C.2 SA_201709C.3 SA_201711A.1 SA_201711A.2 
  0.34428146   0.51288916   0.34782554   0.26759727   0.30495660   0.38470237   0.39976843   0.29197506   0.25564084 
SA_201711A.3 SA_201711C.1 SA_201711C.2 SA_201711C.3 TR_201703A.1 TR_201703A.2 TR_201703A.3 TR_201703B.1 TR_201703B.2 
  0.69659749   0.40263503   0.36079066   0.42258449   0.28690118   0.30483232   0.36887260   0.27470423   0.21341219 
TR_201703B.3 TR_201703C.1 TR_201703C.2 TR_201703C.3 TR_201709A.1 TR_201709A.2 TR_201709A.3 TR_201709B.1 TR_201709B.2 
  0.32103410   0.32434053   0.30302932   0.20319089   0.42016114   0.38673967   0.24437521   0.39704547   0.27150160 
TR_201709B.3 TR_201709C.1 TR_201709C.2 TR_201709C.3 TR_201711A.1 TR_201711A.2 TR_201711A.3 TR_201711C.1 TR_201711C.2 
  0.20033662   0.40464089   0.35630032   0.32446302   0.37615162   0.44181006   0.56579542   0.42424767   0.39447173 
TR_201711C.3 TW_201709A.1 TW_201709A.2 TW_201709A.3 TW_201709B.1 TW_201709B.2 TW_201709B.3 TW_201709C.1 TW_201709C.2 
  0.49610701   0.40776606   0.36526207   0.43733658   0.29452128   0.34268971   0.36638265   0.35102559   0.32632103 
TW_201709C.3 TW_201711A.1 TW_201711A.2 TW_201711A.3 TW_201711B.1 TW_201711B.2 TW_201711B.3 TW_201711C.1 TW_201711C.2 
  0.24594621   0.42210920   0.56421051   0.42146032   0.27757926   0.36819359   0.38233706   0.22374190   0.39372018 
TW_201711C.3 LL_201710B.1 LL_201710B.2 LL_201710B.3 LL_201710C.1 LL_201710C.2 LL_201801A.1 LL_201801A.2 LL_201801A.3 
  0.36200478   0.45601005   0.37219419   0.52013086   0.40010829   0.40010829   0.47483609   0.38875689   0.34757231 
LL_201801B.1 LL_201801B.2 LL_201801B.3 LL_201801C.1 LL_201801C.2 LL_201801C.3 LL_201803A.1 LL_201803A.2 LL_201803A.3 
  0.44365904   0.42948559   0.39678749   0.48964812   0.38989510   0.40033187   0.39725083   0.45922082   0.51017170 
LL_201803B.1 LL_201803B.2 LL_201803B.3 LL_201803C.1 LL_201803C.2 LL_201803C.3 LL_201805A.1 LL_201805A.2 LL_201805A.3 
  0.52788641   0.38470084   0.41359849   0.41798497   0.43011507   0.39597526   0.27057211   0.44704785   0.36526226 
LL_201805B.1 LL_201805B.2 LL_201805B.3 LL_201805C.1 LL_201805C.2 LL_201805C.3 LL_201807A.1 LL_201807A.2 LL_201807A.3 
  0.31968451   0.40926363   0.29023037   0.23004378   0.40913361   0.27166182   0.35498869   0.33911301   0.37514140 
LL_201807B.1 LL_201807B.2 LL_201807B.3 LL_201807C.1 LL_201807C.2 LL_201807C.3 PO_201801A.1 PO_201801A.2 PO_201801A.3 
  0.38492638   0.44617453   0.40303904   0.39521449   0.36847651   0.35585727   0.41376244   0.43980797   0.46446040 
PO_201801B.1 PO_201801B.2 PO_201801B.3 PO_201801C.1 PO_201801C.2 PO_201801C.3 PO_201803A.1 PO_201803A.2 PO_201803A.3 
  0.44717274   0.50609542   0.41916872   0.37395555   0.35126585   0.35056166   0.33937889   0.66503754   0.31771629 
PO_201803B.1 PO_201803B.2 PO_201803B.3 PO_201803C.1 PO_201803C.2 PO_201803C.3 PO_201805A.1 PO_201805A.2 PO_201805A.3 
  0.39921752   0.50327269   0.29846570   0.26958102   0.36846599   0.32504740   0.20787672   0.29576918   0.33659144 
PO_201805B.1 PO_201805B.2 PO_201805B.3 PO_201805C.1 PO_201805C.3 PO_201807A.1 PO_201807A.3 PO_201807B.1 PO_201807B.2 
  0.15277957   0.40404185   0.17131626   0.21529400   0.21529400   0.26729890   0.26729890   0.26230501   0.27780666 
PO_201807B.3 PO_201807C.1 PO_201807C.3 SA_201801A.1 SA_201801A.2 SA_201801A.3 SA_201801B.1 SA_201801B.2 SA_201801B.3 
  0.24149798   0.29435501   0.29435501   0.34322437   0.38055694   0.37830349   0.33384709   0.35019003   0.32689399 
SA_201801C.1 SA_201801C.2 SA_201801C.3 SA_201803A.1 SA_201803A.2 SA_201803A.3 SA_201803B.1 SA_201803B.2 SA_201803B.3 
  0.41283419   0.35481154   0.39044460   0.31680074   0.36624647   0.29069167   0.46765723   0.29346356   0.27191597 
SA_201803C.1 SA_201803C.2 SA_201803C.3 SA_201805A.1 SA_201805A.2 SA_201805A.3 SA_201805B.1 SA_201805B.2 SA_201805B.3 
  0.28212755   0.30154490   0.25190997   0.35201148   0.31093295   0.23391785   0.25581609   0.35125467   0.24293564 
SA_201805C.1 SA_201805C.2 SA_201805C.3 SA_201807A.2 SA_201807A.3 SA_201807B.1 SA_201807B.2 SA_201807B.3 SA_201807C.1 
  0.18367954   0.36957058   0.20466409   0.26160759   0.26160759   0.43231385   0.34158907   0.32004285   0.43497304 
SA_201807C.2 SA_201807C.3 TR_201801A.1 TR_201801A.2 TR_201801A.3 TR_201801B.1 TR_201801B.2 TR_201801B.3 TR_201801C.1 
  0.33807401   0.36963061   0.43585312   0.35803229   0.33323115   0.43113413   0.47746119   0.35498717   0.40107881 
TR_201801C.2 TR_201801C.3 TR_201803A.1 TR_201803A.2 TR_201803A.3 TR_201803B.1 TR_201803B.2 TR_201803B.3 TR_201803C.1 
  0.34318877   0.44774547   0.37630648   0.26172496   0.46540925   0.34132203   0.30491044   0.24726576   0.31232700 
TR_201803C.2 TR_201803C.3 TR_201805A.1 TR_201805A.2 TR_201805A.3 TR_201805B.1 TR_201805B.2 TR_201805B.3 TR_201805C.1 
  0.36634969   0.33088446   0.29852685   0.45471562   0.42680875   0.26015141   0.22231166   0.31442933   0.27020586 
TR_201805C.2 TR_201805C.3 TR_201807A.1 TR_201807A.2 TR_201807A.3 TR_201807B.1 TR_201807B.2 TR_201807B.3 TR_201807C.1 
  0.51255521   0.25408872   0.40951576   0.34973849   0.30016724   0.31013691   0.39837080   0.27234644   0.36667909 
TR_201807C.2 TR_201807C.3 TW_201801A.1 TW_201801A.2 TW_201801A.3 TW_201801B.1 TW_201801B.2 TW_201801B.3 TW_201801C.1 
  0.26485424   0.28847145   0.26409554   0.41165353   0.31252466   0.42396978   0.39415653   0.35929665   0.25495471 
TW_201801C.2 TW_201801C.3 TW_201803A.1 TW_201803A.2 TW_201803A.3 TW_201803B.1 TW_201803B.2 TW_201803B.3 TW_201803C.1 
  0.39970791   0.23136153   0.34567386   0.39890928   0.29058667   0.29981460   0.30015425   0.31707123   0.31838539 
TW_201803C.2 TW_201803C.3 TW_201805A.1 TW_201805A.2 TW_201805A.3 TW_201805B.1 TW_201805B.2 TW_201805B.3 TW_201805C.1 
  0.19972135   0.21277101   0.36535580   0.39694080   0.43662486   0.45913073   0.44492963   0.45837991   0.51992699 
TW_201805C.2 TW_201805C.3 TW_201807A.1 TW_201807A.2 TW_201807A.3 TW_201807B.1 TW_201807B.2 TW_201807B.3 TW_201807C.1 
  0.19071669   0.17632152   0.25486977   0.26029474   0.40987919   0.25639986   0.16238746   0.25383933   0.28148478 
TW_201807C.3 CP_201801A.1 CP_201801A.2 CP_201801A.3 CP_201801B.1 CP_201801B.2 CP_201801B.3 CP_201801C.1 CP_201801C.2 
  0.28148478   0.46838362   0.60798155   0.42381648   0.54719376   0.44557946   0.33814904   0.48226179   0.42301806 
CP_201801C.3 CP_201803A.1 CP_201803A.2 CP_201803A.3 CP_201803B.1 CP_201803B.2 CP_201803B.3 CP_201803C.1 CP_201803C.2 
  0.52646432   0.37547683   0.42643411   0.40372276   0.43293759   0.29018671   0.27049297   0.42976560   0.32824542 
CP_201803C.3 CP_201805A.1 CP_201805A.2 CP_201805B.1 CP_201805B.2 CP_201805B.3 CP_201805C.1 CP_201805C.2 CP_201805C.3 
  0.30304161   0.41508044   0.41508044   0.51521132   0.44276395   0.38169397   0.52261528   0.37612748   0.39763700 
CP_201807A.1 CP_201807A.2 CP_201807A.3 CP_201807B.1 CP_201807B.2 CP_201807B.3 CP_201807C.1 CP_201807C.2 CP_201807C.3 
  0.36703748   0.34679158   0.31771078   0.33890308   0.35776184   0.37889889   0.23776643   0.21772717   0.22228416 
FH_201801A.1 FH_201801A.2 FH_201801A.3 FH_201801B.1 FH_201801B.2 FH_201801C.1 FH_201801C.2 FH_201801C.3 FH_201803A.1 
  0.22170096   0.29616715   0.15430326   0.24672380   0.24672380   0.55894561   0.27000833   0.35200297   0.38247998 
FH_201803A.2 FH_201803A.3 FH_201803B.1 FH_201803B.2 FH_201803B.3 FH_201803C.1 FH_201803C.2 FH_201803C.3 FH_201805A.1 
  0.35944097   0.25766540   0.29907689   0.31358642   0.34990214   0.41459343   0.39403951   0.44417043   0.41581585 
FH_201805A.2 FH_201805A.3 FH_201805B.1 FH_201805B.2 FH_201805B.3 FH_201805C.1 FH_201805C.2 FH_201805C.3 FH_201807A.1 
  0.37400734   0.44746775   0.43024264   0.41308045   0.42925367   0.48615993   0.38203457   0.42145758   0.28473096 
FH_201807A.2 FH_201807A.3 FH_201807B.1 FH_201807B.2 FH_201807B.3 FH_201807C.1 FH_201807C.2 FH_201807C.3 LK_201801A.1 
  0.23057869   0.26977849   0.23104736   0.24195078   0.20292586   0.23980991   0.23118261   0.22521523   0.47988831 
LK_201801A.2 LK_201801A.3 LK_201801B.1 LK_201801B.2 LK_201801B.3 LK_201803A.1 LK_201803A.2 LK_201803A.3 LK_201803B.1 
  0.45300945   0.46873130   0.43226538   0.49581928   0.32645683   0.49255834   0.32651039   0.38882879   0.43339563 
LK_201803B.2 LK_201803B.3 LK_201803C.1 LK_201803C.2 LK_201803C.3 LK_201805A.1 LK_201805A.2 LK_201805A.3 LK_201805B.1 
  0.31307940   0.38094595   0.31812828   0.36584087   0.23524932   0.53950018   0.48575681   0.43766863   0.51803510 
LK_201805B.2 LK_201805B.3 LK_201805C.1 LK_201805C.2 LK_201805C.3 LK_201807A.1 LK_201807A.2 LK_201807A.3 LK_201807B.1 
  0.36246334   0.48127971   0.44164127   0.47156873   0.38190732   0.28425998   0.24809516   0.27360448   0.41958509 
LK_201807B.2 LK_201807B.3 LK_201807C.1 LK_201807C.2 LK_201807C.3 
  0.34999948   0.34744268   0.22149067   0.24829432   0.30292554 
to_write_discarded <- as.tibble(all_distances[outliers]) %>% rownames_to_column("sample") %>% dplyr::select(sample, 
                                                                                                     distance_to_centroid = value)
to_write_discarded <- to_write_discarded %>% bind_rows(tibble(sample = discard.1,
                                                              distance_to_centroid = NA))
write_csv(to_write_discarded ,"discared_samples.csv")

# Who passes this filter

all_distances %>%
  as.tibble() %>% 
  mutate(sample = names(all_distances)) %>% 
  filter(!sample %in% to_write_discarded$sample) %>% 
  separate(sample, into = "event", sep = -3) %>% 
  group_by(event) %>% 
  summarise(cases = n()) %>% 
  separate(event, into = c("Site", "Date"), remove = F) %>% 
  filter(Date != "201703") %>% 
  ggplot()+
  geom_raster(aes(x= Date, y = Site, fill = cases))+
  geom_text(aes(x= Date, y = Site, label = cases))

NA
NA
NA

Finally, let’s remove these samples from the dataset


ASV.nested %>% 
  mutate(Step4.tibble = map (Step3.tibble,  ~ filter(.,! sample %in% to_write_discarded$sample))) -> ASV.nested

ASV.nested %>% 
  transmute(Miseq_run, Summary.1 = map(Step4.tibble, ~ how.many(ASVtable = .,round = "5.PCR.dissimilarity"))) %>% 
  left_join(ASV.summary) %>% 
  mutate(Summary   = map2(Summary, Summary.1, bind_rows)) %>%
  dplyr::select(-Summary.1) -> ASV.summary 
Joining, by = "Miseq_run"

Exporting the output

We will export the final cleaned table with four columns (Miseq_run, sample, Hash, nReads)

input <- read_csv("../Output/Hash_Key_all_together.csv")
Parsed with column specification:
cols(
  Hash = col_character(),
  Sequence = col_character()
)

Summary of the cleanup process


ASV.summary %>% 
  unnest() %>% 
  ggplot(aes(x=Stage, y=number, fill = Stat))+
    geom_line(aes(group = Miseq_run, color = Miseq_run))+
  facet_grid(Stat~., scales = "free")+
  theme(axis.text.x = element_text(angle = 45, hjust =1))#,
                                 
LS0tCnRpdGxlOiAiRGVub2lzaW5nIGFuZCBkZWNvbnRhbWluYXRpbmcgdjIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQpBZnRlciBydW5uaW5nIHRoZSBkZW11bHRpcGxleGVyX2Zvcl9kYWRhMiAoaHR0cDovL2dpdGh1Yi5jb20vcmFtb25nYWxsZWdvL2RlbXVsdGlwbGV4ZXJfZm9yX2RhZGEyKSwgd2UgaGF2ZSB0byBkZW5vaXNlIHRoZSB3aG9sZSBkYXRhc2V0LiBXZSB3aWxsIGRvIHRoaXMgYnkgdXNpbmcgNCBkaWZmZXJlbnQgcHJvY2Vzc2VzOgoKCiAgKiAqKkVzdGltYXRpb24gb2YgKlRhZy1qdW1waW5nKiBvciBpbmRpY2VzICpjcm9zcy10YWxrKiAqKi4gV2UgcnVuIG11bHRpcGxlIHNhbXBsZXMgb24gZWFjaCBNaVNlcSBydW4uIFRoZXNlIGFyZSBpZGVudGlmaWVkIGJ5IHR3byBzZXRzIG9mIG1vbGVjdWxhciBiYXJjb2Rlcy4gVGhlcmUgaXMgdGhlIHBvdGVudGlhbCBvZiBzb21lIHNlcXVlbmNlcyB0byBiZSBhc3NpZ25lZCB0byB0aGUgd3Jvbmcgc2FtcGxlLCB3aGljaCBpcyBhIGJ1bW1lci4gVG8gZXN0aW1hdGUgaG93IG1hbnkgcmVhZHMgZGlkIHRoaXMsIG9uIGVhY2ggTWlTZXEgcnVuIHdlIGFkZGVkIHNvbWUgc2FtcGxlcyB3aG9zZSBjb21wb3NpdGlvbiBpcyBrbm93biBhbmQgZXh0cmVtZWx5IHVubGlrZWx5IHRvIGJlIHByZXNlbnQgaW4gdGhlIGVudmlyb21lbnRhbCBzYW1wbGVzIHN0dWRpZWQuIEFTIGEgcmVzdWx0IG9mIHRoaXMgKipUYWctanVtcGluZyoqLCBzb21lIG9mIHRoZSBwb3NpdGl2ZSBjb250cm9sIHNlcXVlbmNlcyBtaWdodCBzaG93IGluIHRoZSBlbnZpcm9ubWVudGFsIHNhbXBsZXMgYW5kIHZpY2V2ZXJzYS4gSW4gb3VyIGNhc2UsIHRoZXNlIHBvc2l0aXZlIGNvbnRyb2xzIGFyZSBtYWRlIG9mIGVpdGhlciBLYW5nYXJvbyBvciBPc3RyaWNoIChhbmQgQWxsaWdhdG9yKS4gVGhlIHByb2Nlc3MgY29uc2lzdHMgb24sIGZvciBlYWNoIHJ1biwgdG8gbW9kZWwgdGhlIGNvbXBvc2l0b24gb2JzZXJ2ZWQgb24gdGhlIHBvc2l0aXZlIGNvbnRyb2xzIGFuZCBzdWJzdHJhY3QgaXQgZnJvbSB0aGUgZW52aXJvbm1lbnRhbCBzYW1wbGVzIGZyb20gdGhhdCBydW4uIFRoZSBvdXRwdXQgd2lsbCBiZSBhIGRhdGFzZXQgd2l0aCB0aGUgc2FtZSBudW1iZXIgb2Ygc2FtcGxlcyBhcyBiZWZvcmUsIGJ1dCB3aXRoIGZld2VyIHJlYWRzIG9mIGNlcnRhaW4gc2VxdWVuY2VzIChBU1ZzKQogIAogICogKipEaXNjYXJkaW5nIHNhbXBsZXMgd2l0aCBleHRyZW1lbHkgbG93IG51bWJlciBvZiByZWFkcyoqLiBTb21ldGltZXMgdGhlIG51bWJlciBvZiByZWFkcyBzZXF1ZW5jZWQgZnJvbSBhIHBhcnRpY3VsYXIgcmVwbGljYXRlIGFyZSByZWFsbHkgbG93LCBhbmQgaGVuY2UgdGhlIHJlbGF0aXZlIHByb3BvcnRpb25zIG9mIEFTVnMgd291bGQgYmUgc2tld2VkLiAKICAKICAqICoqRnVsbCBjbGVhcmFuY2UgZnJvbSBQb3NpdGl2ZSBjb250cm9sIGluZmx1ZW5jZSoqLiBUSGlzIHByb2Nlc3MgYWxzbyB0YWtlcyBhZHZhbnRhZ2Ugb2YgdGhlIGtub3duIGNvbXBvc2l0aW9uIG9mIHRoZSBwb3NpdGl2ZSBjb250cm9scy4gRWFjaCBBU1YgZm91bmQgaW4gdGhlIHBvc2l0aXZlIGNvbnRyb2xzIHdpdGggYSBoaWdoZXIgYWJ1bmRhY2UgaW4gdGhlbSB0aGFuIGluIHRoZSByZXN0IG9mIHRoZSBzYW1wbGVzIHdpbGwgYmUgbGFiZWxsZWQgYXMgICoqUG9zaXRpdmUqKiBhbmQgcmVtb3ZlZCBmcm9tIHRoZSBlbnZpcm9ubWVudGFsIGRhdGFzZXQuIFRoZSBvdXRwdXQgd2lsbCBiZSBhIGRhdGFzZXQgd2l0aCB0aGUgc2FtZSBudW1iZXIgb2Ygc2FtcGxlcyBhcyBiZWZvcmUgYnV0IHdpdGggZmV3ZXIgQVNWcy4KICAKICAqICoqT2NjdXBhbmN5IG1vZGVsbGluZyoqIC4gSXMgdGhlIHByZXNlbmNlIG9mIGEgQVNWIGEgcmVmbGVjdGlvbiBvZiBhIGJpb2xvZ2ljYWwgcmVhbGl0eSBvciBsaWtlbHkgYSBQQ1IgYXJ0aWZhY3Q/IFRoaXMgbWF5IHNlZW0gdHJpdmlhbCBpbiBleHRyZW1lIGNhc2VzIChhbiBBU1YgdGhhdCBvbmx5IGFwcGVhcnMgaW4gb25lIFBDUiByZXBsaWNhdGUgaW4gdGhlIHdob2xlIGRhdGFzZXQpIGJ1dCBob3cgdG8gZGlzY3JpbWluYXRlIGJldHdlZW4gUENSIGFydGlmYWN0cyBmcm9tIHJhcmUgYnV0IHJlYWwgb3JnYW5pc21zPyBXZSB1c2UgT2NjdXBhbmN5IG1vZGVsbGluZyB0byBkZXRlcm1pbmUgaWYgdGhlIHBhdHRlcm4gb2YgcHJlc2VuY2Ugb2YgYSBBU1YgaW4gYSBkYXRhc2V0IHJlZmxlY3RzIHRoYXQuIFRoZSBvdXRwdXQgb2YgdGhpcyBwcm9jZWR1cmUgd2lsbCBiZSBhIGRhdGFzZXR3aXRoIHRoZSBzYW1lIG51bWJlciBvZiBzYW1wbGVzIGFzIGJlZm9yZSBidXQgd2l0aCBmZXdlciBBU1ZzLgogIAogICogKipEaXNzaW1pbGFyaXR5IGJldHdlZW4gUENSIHJlcGxpY2F0ZXMqKi4gVGhlIHdvcmtmbG93IHRoYXQgbGVhZHMgdG8gdGhlIHNlcXVlbmNpbmcgb2YgYSBwYXJ0aWN1bGFyIHNhbXBsZSBpcyBzdWJqZWN0IHRvIG1hbnkgc3RvY2hhdGljIHByb2Nlc3NlcywgYW5kIGl0IGlzIG5vdCB1bmxpa2VseSB0aGF0IHRoZSBjb21wb3NpdGlvbiByZXRyaWV2ZWQgaXMgdmVyeSBkaWZmZXJlbnQgZm9yIHRoZSBvcmlnaW5hbCBjb21tdW5pdHkuIEEgd2F5IHRvIGVuc3VyZSB0aGF0IHRoaXMgZGlmZmVyZW5jZSBpcyBtaW5pbWFsIGlzIHRocm91Z2ggdGhlIHNlcGFyYXRlIGFuYWx5c2lzIG9mIGVhY2ggUENSIHJlcGxpY2F0ZS4gV2UgdXNlZCB0aGF0IGFwcHJvYWNoIGFuZCBtb2RlbGVkIHRoZSBkaXNzaW1pbGFyaXR5IGJldHdlZW4gZWFjaCBQQ3IgcmVwbGljYXRlIGFuZCB0aGUgZ3JvdXAgY2VudHJvaWQuIFRoaXMgd2F5IG9mIG1vZGVsaW5nIHRoZSBkaXNzaW1pbGFyaXR5IGFsbG93cyB1cyB0byBkaXNjYXJkIHRob3NlIFBDUiByZXBsaWNhdGUgdGhhdCB3b24ndCBmaXQgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgZGlzc2ltaWxhcml0aWVzLiBUaGUgb3V0cHV0IG9mIHRoaXMgcHJvY2VkdXJlIHdpbGwgYmUgYSBkYXRhc2V0IHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mICoqSGFzaGVzKiogYXMgYmVmb3JlIGJ1dCB3aXRoIGZld2VyICoqc2FtcGxlcyoqLgogIAogIApBcyB3aXRoIGV2ZXJ5dGhpbmcsIHdlIHdpbGwgc3RhcnQgdGhlIHByb2Nlc3MgYnkgbG9hZGluZyB0aGUgcmVxdWlyZWQgcGFja2FnZXMgYW5kIGRhdGFzZXRzLgoKIyBMb2FkIHRoZSBkYXRhc2V0IGFuZCBtZXRhZGF0YQoKCgpgYGB7ciBsb2FkIGxpYnJhcmllcywgaW5jbHVkZT1GQUxTRX0KIGtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UpCgogbGlicmFyeSAodGlkeXZlcnNlKQogbGlicmFyeSAodmVnYW4pCiAjbGlicmFyeSAoTUFTUykKIGxpYnJhcnkgKHByb3h5KQogbGlicmFyeSAocmVzaGFwZTIpCiBsaWJyYXJ5IChzZXFpbnIpCiBsaWJyYXJ5KHBhdGNod29yaykKCmBgYAoKV2Ugd2lsbCBsb2FkIHRoZSBBU1YgdGFibGUgYW5kIHRoZSBtZXRhZGF0YSBmaWxlLiBUaGV5IGFyZSBpbiB0aGUgc2FtZSBmb2xkZXIgc28gd2UgdXNlIGBsaXN0LmZpbGVzYCB0byBhY2Nlc3MgdGhlbSBhbmQgYSBuZWF0IGNvbWJpbmF0aW9uIG9mIGBiaW5kLnJvd3NgIGFuZCBgbWFwKHJlYWRfY3N2KWAuIAoKYGBge3IgbG9hZCBkYXRhc2V0cyAtIHdlIHdpbGwgYmUgZG9pbmcgdGhhdCBmb3IgYWxsIHJ1bnN9CgpMb2NhbF9mb2xkZXIgPC0gIi4uL0lucHV0LzEuU2VxdWVuY2luZ19ydW5zX291dHB1dCIKCmFsbC5hc3ZzIDwtIGxpc3QuZmlsZXMocGF0aCA9IExvY2FsX2ZvbGRlciwgcGF0dGVybiA9ICJeQVNWX3RhYmxlLmNzdiIsIHJlY3Vyc2l2ZSA9IFQsIGZ1bGwubmFtZXMgPSBUKQphbGwubWV0YWRhdGEgPC0gbGlzdC5maWxlcyhwYXRoID0gTG9jYWxfZm9sZGVyLCBwYXR0ZXJuID0gIl5tZXRhZGF0YS5jc3YiLCByZWN1cnNpdmUgPSBULCBmdWxsLm5hbWVzID0gVCkKYWxsLmhhc2hlcyA8LSBsaXN0LmZpbGVzKHBhdGggPSBMb2NhbF9mb2xkZXIsIHBhdHRlcm4gPSAiXkhhc2hfa2V5LmNzdiIsIHJlY3Vyc2l2ZSA9IFQsIGZ1bGwubmFtZXMgPSBULGlnbm9yZS5jYXNlID0gVCkKCkFTVi50YWJsZSA8LSBiaW5kX3Jvd3MobWFwKGFsbC5hc3ZzLCByZWFkX2NzdiksIC5pZCA9ICJNaXNlcV9ydW4iKQoKbWV0YWRhdGEgPC0gYmluZF9yb3dzKG1hcChhbGwubWV0YWRhdGEsIGZ1bmN0aW9uKHgpIHsKICByZWFkX2Nzdih4KSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoInNhbXBsZV9pZCIsICJwcmlfaW5kZXhfbmFtZSIsICJUYWciKQogIH0pLC5pZCA9ICJNaXNlcV9ydW4iKQoKSGFzaC5rZXkgPC0gYmluZF9yb3dzKG1hcChhbGwuaGFzaGVzLCByZWFkX2NzdikpCgpIYXNoLmtleSAlPiUgCiAgZGlzdGluY3QoSGFzaCwgLmtlZXBfYWxsID0gVCkgLT4gSGFzaC5rZXkKYGBgCgoKIyMgRGF0YSBDbGVhbnVwIC0gRG9uJ3QgYWN0IGxpa2UgeW91IGRvbid0IG5lZWQgdGhpcwoKQSBmZXcgdGhpbmdzIHdlIGNoZWNrIGZvcjogVGhhdCAqKm5vIHNhbXBsZSBhcHBlYXJzIHR3aWNlKiogaW4gdGhlIG1ldGFkYXRhLiBUaGF0IHRoZSBtZXRhZGF0YSAqKnVzZXMgVGFnXzAxIGluc3RlYWQgb2YgVGFnXzEqKiAoc28gaXQgY2FuIGJlIHNvcnRlZCBhbHBoYWJldGljYWxseSkuIFRoYXQgKip0aGUgc3RydWN0dXJlKiogU2l0ZV9ZWVlZTU1bQS1DXS5bMS0zXSAqKmlzIHRoZSBzYW1lKiogYWNyb3NzIHRoZSBkYXRhc2V0LgoKYGBge3IgZGF0YSBjbGVhbmluZ30KCiMgQ2hlY2sgdGhhdCBubyBzYW1wbGUgYXBwZWFycyBtb3JlIHRoYW4gb25jZSBpbiB0aGUgbWV0YWRhdGEKCm1ldGFkYXRhICU+JSAKICBncm91cF9ieShzYW1wbGVfaWQpICU+JQogIHN1bW1hcmlzZSh0b3QgPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2ModG90KSkgIyBTYW1wbGVzIG9ubHkgYXBwZWFyIG9uY2UKCiMgV2Ugc2hvdWxkIGNoYW5nZSBUYWdfMSBmb3IgVGFnXzAxCgptZXRhZGF0YSAlPiUKICBtdXRhdGUoVGFnID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoVGFnLCAiXFxfWzAtOV17MX0kIikgICAgICAgfiAgICAgc3RyX3JlcGxhY2UoVGFnLCAiVGFnXyIsICJUYWdfMCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+ICAgICBUYWcgICkpIC0+IG1ldGFkYXRhCgoKCm1ldGFkYXRhICU+JSBtdXRhdGUoeD0gc3RyX2NvdW50KHNhbXBsZV9pZCwgcGF0dGVybiA9ICJfIikpICU+JSBhcnJhbmdlKGRlc2MoeCkpICMgTGlsbGl3YXVwIDIwMTcxMCBoYXMgMiB1bmRlcnNjb3JlcywgcnVuIDMgbWV0YWRhdGEgaGFzIG5vIHVuZGVyc2NvcmUKCiAgIApBU1YudGFibGUgJT4lIGRpc3RpbmN0KHNhbXBsZSkgJT4lIG11dGF0ZSh4PSBzdHJfY291bnQoc2FtcGxlLCBwYXR0ZXJuID0gIl8iKSkgJT4lIGFycmFuZ2UoZGVzYyh4KSkgIyBBbHNvIGluIHRoZSBBU1YgdGFibGUKCiMgRml4IGl0IGhlcmUKCm1ldGFkYXRhICU+JSBtdXRhdGUoeD0gc3RyX2NvdW50KHNhbXBsZV9pZCwgcGF0dGVybiA9ICJfIikpICU+JQogIG11dGF0ZShzYW1wbGVfaWQgPSBjYXNlX3doZW4oeCA9PSAyIH4gc3RyX3JlcGxhY2Uoc2FtcGxlX2lkLCBwYXR0ZXJuID0gIl9CIiwgcmVwbGFjZW1lbnQgPSAiQiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgIH4gc2FtcGxlX2lkKSkgJT4lIAogIGRwbHlyOjpzZWxlY3QoLXgpIC0+IG1ldGFkYXRhCgpBU1YudGFibGUgJT4lIG11dGF0ZSh4PSBzdHJfY291bnQoc2FtcGxlLCBwYXR0ZXJuID0gIl8iKSkgJT4lCiAgbXV0YXRlKHNhbXBsZSA9IGNhc2Vfd2hlbih4ID09IDIgfiBzdHJfcmVwbGFjZShzYW1wbGUsIHBhdHRlcm4gPSAiX0IiLCByZXBsYWNlbWVudCA9ICJCIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFICAgfiBzYW1wbGUpKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgteCkgLT4gQVNWLnRhYmxlIAoKIyBEb25lCgojIENoYW5nZSBLYW5nYXJvby40IGZvciBLYW5nYXJvby4zLiBBbHNvIENoYW5nZSBPc3RyaWNoMSBPc3RyaWNoMiBhbmQgT3N0cmljaDMgZm9yIC4xLCAuMiAuMwoKbWV0YWRhdGEgJT4lIAogIG11dGF0ZSAoc2FtcGxlX2lkID0gY2FzZV93aGVuIChzdHJfZGV0ZWN0KHNhbXBsZV9pZCwgIk9zdHJpY2hbMTIzXSIpIH4gc3RyX3JlcGxhY2Uoc2FtcGxlX2lkLCAiT3N0cmljaCIsICJPc3RyaWNoXFwuIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZCA9PSAiS2FuZ2Fyb28uNCIgICAgICAgICAgICAgfiAiS2FuZ2Fyb28uMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiBzYW1wbGVfaWQpKSAtPiBtZXRhZGF0YQpBU1YudGFibGUgJT4lCiAgbXV0YXRlIChzYW1wbGUgPSBjYXNlX3doZW4gKHN0cl9kZXRlY3Qoc2FtcGxlLCAiT3N0cmljaFsxMjNdIikgfiBzdHJfcmVwbGFjZShzYW1wbGUsICJPc3RyaWNoIiwgIk9zdHJpY2hcXC4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlID09ICJLYW5nYXJvby40IiAgICAgICAgICB+ICJLYW5nYXJvby4zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IHNhbXBsZSkpIC0+IEFTVi50YWJsZQoKIyBET05FCgojIEFkZCB1bmRlcnNjb3JlIHRvIHJ1bjMKCkFTVi50YWJsZSAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc2FtcGxlLCAiW0EtWl1bQS1aXTIwMTciKSkgJT4lIAogIGRpc3RpbmN0KE1pc2VxX3J1bikgIyBpdCBvbmx5IGFmZmVjdHMgb25lIHJ1bgoKQVNWLnRhYmxlICU+JSAKICBtdXRhdGUgKHNhbXBsZSA9IGNhc2Vfd2hlbihzdHJfZGV0ZWN0KHNhbXBsZSwgIltBLVpdW0EtWl0yMDE3IikgICAgfiAgICBzdHJfcmVwbGFjZShzYW1wbGUsIjIwMTciLCAiXzIwMTciICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gICAgc2FtcGxlKSkgLT4gQVNWLnRhYmxlCgojIEFTViB0YWJsZSBmcm9tIFJ1biAxIGhhcyB0aGUgZnVsbCBkYXRlIGluc3RlYWQgb2YganVzdCBZWVlZTU0KCkFTVi50YWJsZSAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc2FtcGxlLCAiX1s6ZGlnaXQ6XXs4fSIpKSAjIFlFUwoKQVNWLnRhYmxlICU+JSAKICBtdXRhdGUoc2FtcGxlID0gY2FzZV93aGVuKHN0cl9kZXRlY3Qoc2FtcGxlLCAiX1s6ZGlnaXQ6XXs4fSIpICAgIH4gIHBhc3RlMChzdHJfcmVwbGFjZShzYW1wbGUsICJfWzpkaWdpdDpdezh9Iiwgc3RyX2V4dHJhY3Qoc2FtcGxlLCJfWzpkaWdpdDpdezZ9IiApKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+ICBzYW1wbGUpKSAtPiBBU1YudGFibGUKCiMgaW4gTWlTZXFSdW4xLCB0aGVyZSBhcmUgZHVwbGljYXRlZCBlbnRyaWVzIG9mIHNvbWUgSGFzaCAvIHNhbXBsZSBjb21ib3MsIHByb2JhYmx5IGJjIHdlIGNvbWJpbmVkIHRoZSBNaXNlcSBhbmQgTWlTZXEgbmFubyBydW5zCgoKQVNWLnRhYmxlICU+JSAKICBncm91cF9ieShNaXNlcV9ydW4sIHNhbXBsZSwgSGFzaCkgJT4lIAogIHN1bW1hcmlzZShuUmVhZHMgPSBzdW0oblJlYWRzKSkgJT4lIAogIHVuZ3JvdXAgLT4gQVNWLnRhYmxlCgoKYGBgCgpUaGUgb3V0cHV0IG9mIHRoaXMgcHJvY2VzcyBhcmUgYSBjbGVhbiBBU1YgdGFibGUgYW5kIGEgY2xlYW4gbWV0YWRhdGEgZmlsZS4KCiMjIENsZWFuaW5nIFByb2Nlc3MgMTogRXN0aW1hdGlvbiBvZiAqVGFnLWp1bXBpbmcqIG9yIHNhbXBsZSAqY3Jvc3MtdGFsayoKCkJlZm9yZSB3ZSBtb2RpZnkgb3VyIGRhdGFzZXRzIG9uIGFueSB3YXksIHdlIGNhbiBjYWxjdWxhdGUgaG93IG1hbnkgc2VxdWVuY2VzIHRoYXQgd2VyZSBvbmx5IHN1cHBvc2VkIHRvIGJlIGluIHRoZSBwb3NpdGl2ZXMgY29udHJvbCBhcHBlYXJlZCBpbiB0aGUgZW52aXJvbm1lbnRhbCBzYW1wbGVzLCBhbmQgaG93IG1hbnkgZGlkIHRoZSBvcHBvc2l0ZS4gRmlyc3Qgd2UgZGl2aWRlIHRoZSBkYXRhc2V0IGludG8gcG9zaXRpdmUgY29udHJvbCBhbmQgZW52aXJvbm1lbnRhbCBzYW1wbGVzLiBBbHNvIGNyZWF0ZSBhbiBvcmRlcmVkIGxpc3Qgb2YgdGhlIEhhc2hlcyBwcmVzZW50IGluIHRoZSBwb3NpdGl2ZSBjb250cm9scywgZm9yIGVhc2Ugb2YgcGxvdHRpbmcKCmBgYHtyIHNwbGl0IGludG8gdHdvfQoKQVNWLnRhYmxlICU+JSAgbXV0YXRlKHNvdXJjZSA9IGNhc2Vfd2hlbihzdHJfZGV0ZWN0KHNhbXBsZSwgIkthbmdhcm9vfEtcXCt8a1xcK3xPc3RyaWNoIikgICAgfiAgICJQb3NpdGl2ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+ICAgIlNhbXBsZXMiKSkgLT4gQVNWLnRhYmxlCgpBU1YudGFibGUgJT4lIAogIGZpbHRlciAoc291cmNlID09ICJQb3NpdGl2ZXMiKSAlPiUgCiAgZ3JvdXBfYnkoSGFzaCkgJT4lIAogIHN1bW1hcmlzZSh0b3QgPSBzdW0oblJlYWRzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3QpKSAlPiUgCiAgcHVsbChIYXNoKSAtPiBnb29kLm9yZGVyCgoKYGBgCgpOb3cgbGV0J3MgY3JlYXRlIGEganVtcGluZyB2ZWN0b3IuIFdoYXQgcHJvcG9ydGlvbiBvZiB0aGUgcmVhZHMgZm91bmQgaW4gdGhlIHBvc2l0aXZlcyBjb250cm9sIGNvbWUgZnJvbSBlbHNld2hlcmUsIGFuZCB3aGF0IHByb3BvcnRpb24gb2YgdGhlIHJlYWRzIGluIHRoZSBzYW1wbGVzIGNvbWUgZnJvbSB0aGUgcG9zaXRpdmVzIGNvbnRyb2wuCgojIyMgU3RlcCAxOiBOZXN0IHRoZSBkYXRhc2V0IGFuZCBzcGxpdCBpdCBpbiBwb3NpdGl2ZXMgYW5kIHNhbXBsZXMKClRvIHN0cmVhbWxpbmUgdGhlIHByb2Nlc3MgYW5kIG1ha2UgaXQgZWFzaWVyIHRvIGV4ZWN1dGUgaXQgc2ltaWxhcmx5IGJ1dCBpbmRlcGVuZGVudGx5IG9uIGVhY2ggTWlzZXEgcnVuLCB3ZSBuZXN0IHRoZSBkYXRhc2V0IGJ5IHJ1bi4gClNvIFN0ZXAxIGlzIGNyZWF0ZSBhIG5lc3RlZCB0YWJsZSBzbyB3ZSBjYW4gcnVuIHRoaXMgYW5hbHlzaXMgb24gZWFjaCBydW4gaW5kZXBlbmRlbnRseS4gCgoKYGBge3IgbmVzdGluZyB0aGUgZGF0YXNldH0KCkFTVi50YWJsZSAlPiUgCiAgZ3JvdXBfYnkoTWlzZXFfcnVuLCBzb3VyY2UpICU+JSAKICBuZXN0KCkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzb3VyY2UsIHZhbHVlc19mcm9tID0gIGRhdGEpIC0+IEFTVi5uZXN0ZWQgCmBgYAoKVGhhdCB3YXNuJ3QgdG9vIGNvbXBsaWNhdGVkLiBMZXQncyBzdGFydCBhIHN1bW1hcnkgZnVuY3Rpb24gdGhhdCBrZWVwcyB0cmFjayBvZiBvdXIgY2xlYW5pbmcgcHJvY2VzcwoKYGBge3Igc3VtbWFyeS5maWxlfQoKaG93Lm1hbnkgPC0gZnVuY3Rpb24oQVNWdGFibGUsIHJvdW5kKXsKICBBU1Z0YWJsZSAlPiUgdW5ncm91cCgpICU+JSAKICAgIHN1bW1hcmlzZShuc2FtcGxlcyA9IG5fZGlzdGluY3Qoc2FtcGxlKSwKICAgICAgICAgICAgICBuSGFzaGVzID0gbl9kaXN0aW5jdChIYXNoKSwKICAgICAgICAgICAgICBuUmVhZHMgPSBzdW0oblJlYWRzKSwgCiAgICAgICAgICAgICAgU3RhZ2UgPSBwYXN0ZTAoIlN0ZXBfIiwgcm91bmQpKSAlPiUgCiAgICBnYXRoZXIoc3RhcnRzX3dpdGgoIm4iKSwgdmFsdWUgPSAibnVtYmVyIiwga2V5ID0gIlN0YXQiKQp9CgpBU1YubmVzdGVkICU+JSAKICB0cmFuc211dGUoTWlzZXFfcnVuLFN1bW1hcnkgPSBtYXAoU2FtcGxlcywgfiBob3cubWFueShBU1Z0YWJsZSA9IC4scm91bmQgPSAwKSkpICAtPiBBU1Yuc3VtbWFyeQoKYGBgCgojIyMgU3RlcCAyOiBNb2RlbCB0aGUgY29tcG9zaXRpb24gb2YgdGhlIHBvc2l0aXZlIGNvbnRyb2xzIG9mIGVhY2ggcnVuIAoKCldlIGNyZWF0ZSBhIHZlY3RvciBvZiB0aGUgY29tcG9zaXRpb24gb2YgZWFjaCBwb3NpdGl2ZSBjb250cm9sIGFuZCBzdWJzdHJhY3QgaXQgZnJvbSB0aGUgZW52aXJvbm1lbnRhbCBzYW1wbGVzIGZyb20gdGhlaXIgcnVucwoKCgpgYGB7ciBqdW1waW5nIHZlY3Rvcn0KCgpBU1YubmVzdGVkICU+JSAKICBtdXRhdGUgKGNvbnRhbS50aWJibGUgPSBtYXAoUG9zaXRpdmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oLngpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC54ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoc2FtcGxlKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZSAoVG90YWxSZWFkc3BlclNhbXBsZSA9IHN1bShuUmVhZHMpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZSAocHJvcG9ydGlvbiA9IG5SZWFkcy9Ub3RhbFJlYWRzcGVyU2FtcGxlKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5IChIYXNoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSAodmVjdG9yX2NvbnRhbWluYXRpb24gPSBtYXggKHByb3BvcnRpb24pKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pICkgLT4gQVNWLm5lc3RlZAoKQVNWLm5lc3RlZCAlPiUgCiAgZ3JvdXBfYnkoTWlzZXFfcnVuKSAlPiUgCiAgc2VsZWN0KGNvbnRhbS50aWJibGUpICU+JSAKICB1bm5lc3QoY29scyA9IGNvbnRhbS50aWJibGUpICMgQ2hlY2sgaG93IGl0IGxvb2tzIGxpa2UKCgoKYGBgCgoKIyMjIFN0ZXAgMzogU3Vic3RyYWN0IHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgcG9zaXRpdmUgY29udHJvbHMgZnJvbSB0aGUgZW52aXJvbm1lbnQgc2FtcGxlcwoKVGhlIGlkZWEgYmVoaW5kIHRoaXMgcHJvY2VkdXJlIGlzIHRoYXQgd2Uga25vdywgZm9yIGVhY2ggcnVuLCBob3cgbWFueSByZWFkcyBmcm9tIGVhY2ggSGFzaCBhcHBlYXJlZCBpbiB0ZWggcG9zaXRpdmUgY29udHJvbHMuIFRoZXNlIGNvbWUgZnJvbSAyIHByb2Nlc3Nlczogc2VxdWVuY2VzIHdlIGtub3cgc2hvdWxkIGFwcGVhciBpbiB0aGUgcG9zaXRpdmUgY29udHJvbHMsIGFuZCBzZXF1ZW5jZXMgdGhhdCBoYXZlICpqdW1wZWQqIGZyb20gdGhlIGVudmlyb25tZW50IHRvIHRoZSBwb3NpdGl2ZSBjb250cm9scy4gV2l0aCB0aGlzIHByb2NlZHVyZSwgd2Ugc3Vic3RyYWN0IGZyb20gZXZlcnkgZW52aXJvbm1lbnRhbCBzYW1wbGUgdGhlIHByb3BvcnRpb24gb2YgcmVhZHMgdGhhdCBqdW1wZWQgZnJvbSBlbHNld2hlcmUuCgpgYGB7ciBjbGVhbmluZyBzdGVwIDF9CkFTVi5uZXN0ZWQgJT4lIAogIG11dGF0ZShjbGVhbmVkLnRpYmJsZSA9IG1hcDIoU2FtcGxlcywgY29udGFtLnRpYmJsZSwgZnVuY3Rpb24oLngsLnkpeyAKICAgIC54ICU+JQogICAgICBncm91cF9ieSAoc2FtcGxlKSAlPiUKICAgICAgbXV0YXRlIChUb3RhbFJlYWRzcGVyU2FtcGxlID0gc3VtIChuUmVhZHMpKSAlPiUKICAgICAgbGVmdF9qb2luKC55LCBieSA9ICJIYXNoIikgJT4lCiAgICAgIG11dGF0ZSAoVXBkYXRlZF9uUmVhZHMgPSBpZmVsc2UgKCFpcy5uYSh2ZWN0b3JfY29udGFtaW5hdGlvbiksICBuUmVhZHMgLSAoY2VpbGluZyh2ZWN0b3JfY29udGFtaW5hdGlvbipUb3RhbFJlYWRzcGVyU2FtcGxlKSksIG5SZWFkcykpICU+JQogICAgICBmaWx0ZXIgKFVwZGF0ZWRfblJlYWRzID4gMCkgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QgKHNhbXBsZSwgSGFzaCwgblJlYWRzID0gVXBkYXRlZF9uUmVhZHMpCiAgICAgIAogICAgCiAgfSkpIC0+IEFTVi5uZXN0ZWQKCkFTVi5uZXN0ZWQgJT4lIAogIGdyb3VwX2J5KE1pc2VxX3J1bikgJT4lIAogIHNlbGVjdChjbGVhbmVkLnRpYmJsZSkgJT4lIAogIHVubmVzdChjbGVhbmVkLnRpYmJsZSkgI0NoZWNrIGhvdyB0aGV5IGxvb2sKCgpgYGAKQWRkIHRoaXMgc3RlcCB0byB0aGUgc3VtbWFyeSB0YWJsZSB3ZSB3ZXJlIGNyZWF0aW5nCgpgYGB7ciBzdW1tYXJ5LmZpbGUuMn0KQVNWLm5lc3RlZCAlPiUgCiAgdHJhbnNtdXRlKE1pc2VxX3J1biwgU3VtbWFyeS4xID0gbWFwKGNsZWFuZWQudGliYmxlLCB+IGhvdy5tYW55KEFTVnRhYmxlID0gLixyb3VuZCA9ICIxLkp1bXAiKSkpICU+JSAKICBsZWZ0X2pvaW4oQVNWLnN1bW1hcnkpICU+JSAKICBtdXRhdGUoU3VtbWFyeSAgID0gbWFwMihTdW1tYXJ5LCBTdW1tYXJ5LjEsIGJpbmRfcm93cykpICU+JQogIGRwbHlyOjpzZWxlY3QoLVN1bW1hcnkuMSkgLT4gQVNWLnN1bW1hcnkgCmBgYAoKIyMgQ2xlYW5pbmcgUHJvY2VzcyAyOiAqKkRpc2NhcmRpbmcgUENSIHJlcGxpY2F0ZXMgd2l0aCBsb3cgbnVtYmVyIG9mIHJlYWRzKioKCldlIHdpbGwgZml0IHRoZSBudW1iZXIgb2YgcmVhZHMgYXNzaWduZWQgdG8gZWFjaCBzYW1wbGUgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uIGFuZCBkaXNjYXJkIHRob3NlIHNhbXBsZXMgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDk1JSBvZiBub3QgZml0dGluZyBpbiB0aGF0IGRpc3RyaWJ1dGlvbi4gVGhlIG91dHB1dCB3b3VsZCBiZSBhIGRhdGFzZXQgd2l0aCBsZXNzIHNhbXBsZXMgYW5kIHBvdGVudGlhbGx5IGxlc3MgbnVtYmVyIG9mIHVuaXF1ZSBIYXNoZXMuCgpgYGB7ciBmaXR0aW5nIG5SZWFkcyBwZXIgc2FtcGxlfQoKQVNWLm5lc3RlZCAlPiUgCiAgc2VsZWN0KE1pc2VxX3J1bixjbGVhbmVkLnRpYmJsZSkgJT4lIAogIHVubmVzdChjbGVhbmVkLnRpYmJsZSkgJT4lIAogIGdyb3VwX2J5KHNhbXBsZSkgJT4lCiAgc3VtbWFyaXNlKHRvdCA9IHN1bShuUmVhZHMpKSAtPiBhbGwucmVwcwoKIyBWaXN1YWxpemUKCmFsbC5yZXBzICU+JSAgCiAgcHVsbCh0b3QpIC0+IHJlYWRzLnBlci5zYW1wbGUKCm5hbWVzKHJlYWRzLnBlci5zYW1wbGUpIDwtIGFsbC5yZXBzICU+JSBwdWxsKHNhbXBsZSkgIAoKbm9ybXBhcmFtcy5yZWFkcyA8LSBNQVNTOjpmaXRkaXN0cihyZWFkcy5wZXIuc2FtcGxlLCAibm9ybWFsIikkZXN0aW1hdGUKCgoKYWxsLnJlcHMgJT4lICAKICBtdXRhdGUocHJvYiA9IHBub3JtKHRvdCwgbm9ybXBhcmFtcy5yZWFkc1sxXSwgbm9ybXBhcmFtcy5yZWFkc1syXSkpIC0+IGFsbC5yZXBzCgojICBwcm9icyA8LSBwbm9ybShhbGxfcGFpcndpc2VfZGlzdGFuY2VzLCBub3JtcGFyYW1zWzFdLCBub3JtcGFyYW1zWzJdKQoKb3V0bGllcnMgPC0gCiAgYWxsLnJlcHMgJT4lIAogIGZpbHRlcihwcm9iIDwgMC4xICYgdG90IDwgbm9ybXBhcmFtcy5yZWFkc1sxXSkKCkFTVi5uZXN0ZWQgJT4lIAogIG11dGF0ZShTdGVwLjEubG93LnJlYWRzID0gbWFwIChjbGVhbmVkLnRpYmJsZSwgfiBmaWx0ZXIoLiwhc2FtcGxlICVpbiUgb3V0bGllcnMkc2FtcGxlKSAlPiUgdW5ncm91cCkpIC0+IEFTVi5uZXN0ZWQKCkFTVi5uZXN0ZWQgJT4lIAogIHRyYW5zbXV0ZShNaXNlcV9ydW4sIFN1bW1hcnkuMSA9IG1hcChTdGVwLjEubG93LnJlYWRzLCB+IGhvdy5tYW55KEFTVnRhYmxlID0gLixyb3VuZCA9ICIyLkxvdy5uUmVhZHMiKSkpICU+JSAKICBsZWZ0X2pvaW4oQVNWLnN1bW1hcnkpICU+JSAKICBtdXRhdGUoU3VtbWFyeSAgID0gbWFwMihTdW1tYXJ5LCBTdW1tYXJ5LjEsIGJpbmRfcm93cykpICU+JQogIGRwbHlyOjpzZWxlY3QoLVN1bW1hcnkuMSkgLT4gQVNWLnN1bW1hcnkgCgpgYGAKCgoKCiMjIENsZWFuaW5nIFByb2Nlc3MgMzogKipGdWxsIGNsZWFyYW5jZSBmcm9tIFBvc2l0aXZlIGNvbnRyb2wgaW5mbHVlbmNlKioKClJlbW92aW5nIHRoZSBIYXNoZXMgdGhhdCBiZWxvbmcgdG8gdGhlIHBvc2l0aXZlIGNvbnRyb2xzLiBGaXJzdCwgZm9yIGVhY2ggSGFzaCB0aGF0IGFwcGVhcmVkIGluIHRoZSBwb3NpdGl2ZSBjb250cm9scywgZGV0ZXJtaW5lIHdoZXRoZXIgYSBzZXF1ZW5jZSBpcyBhIHRydWUgcG9zaXRpdmUgb3IgYSB0cnVlIGVudmlyb25tZW50LiBGb3IgZWFjaCBIYXNoLCB3ZSB3aWxsIGNhbGN1bGF0ZSwgbWF4aW11bSwgbWVhbiBhbmQgdG90YWwgbnVtYmVyIG9mIHJlYWRzIGluIGJvdGggcG9zaXRpdmUgYW5kIHNhbXBsZXMsIGFuZCB0aGVuIHdlIHdpbGwgdXNlIHRoZSBmb2xsb3dpbmcgZGVjaXNzaW9uIHRyZWU6CgogICogSWYgYWxsIHRocmVlIHN0YXRpc3RpY3MgYXJlIGhpZ2hlciBpbiBvbmUgb2YgdGhlIGdyb3Vwcywgd2Ugd2lsbCBsYWJlbCBpdCBlaXRoZXIgb2YgRW52aXJvbm1lbnRhbCBvciBQb3NpdGl2ZSBjb250cm9sIGluZmx1ZW5jZS4KICAKICAqIElmIHRoZXJlIGFyZSBjb25mbGljdGluZyByZXN1bHRzLCB3ZSB3aWxsIHVzZSB0aGUgSGFzaGVzLiB0byBzZWUgaWYgdGhleSBiZWxvbmcgdG8gZWl0aGVyIHRoZSBtYXhpbXVtIGFidW5kYW5jZSBvZiBhIEhhc2ggaXMgaW4gYSBwb3NpdGl2ZSwgdGhlbiBpdCBpcyBhIHBvc2l0aXZlLCBvdGhlcndpc2UgaXMgYSByZWFsIHNlcXVlbmNlIGZyb20gdGhlIGVudmlyb25tZW50LgoKCk5vdywgZm9yIGVhY2ggSGFzaCBpbiBlYWNoIHNldCBvZiBwb3NpdGl2ZXMgY29udHJvbHMsIGNhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiByZWFkcyB0aGF0IHdlcmUgbWlzc2FzaWduZWQgLSB0aGV5IGFwcGVhcmVkIHNvbWV3aGVyZSB0aGV5IHdlcmUgbm90IGV4cGVjdGVkLgpXZSB3aWxsIGRpdmlkZSB0aGF0IHByb2Nlc3MgaW4gdHdvOiBmaXJzdCAuIEEgc2Vjb25kIHN0ZXAgd291bGQgYmUgdG8gY3JlYXRlIGEgY29sdW1uIG5hbWVkIHByb3BvcnRpb24gc3dpdGNoZWQsIHdoaWNoIHN0YXRlcyB0aGUgcHJvcG9ydGlvbiBvZiByZWFkcyBmcm9tIG9uZSBIYXNoIHRoYXQganVtcGVkIGZyb20gdGhlIGVudmlyb25tZW50IHRvIGEgcG9zaXRpdmUgY29udHJvbCBvciB2aWNldmVyc2EuIFRoZSBpZGVhIGlzIHRoYXQgYW55IHByZXNlbmNlIGJlbG93IGEgdGhyZXNob2xkIGNhbiBiZSBhcmd1YWJseSBiZWxvbmcgdG8gdGFnIGp1bXBpbmcuCgpgYGB7ciByZWFsIG9yIHBvc2l0aXZlfQoKCkFTVi50YWJsZSAlPiUgCiAgZmlsdGVyIChIYXNoICVpbiUgZ29vZC5vcmRlcikgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlKSAlPiUgCiAgbXV0YXRlKHRvdC5yZWFkcyA9IHN1bShuUmVhZHMpKSAlPiUgCiAgZ3JvdXBfYnkoSGFzaCxzYW1wbGUpICU+JSAKICBtdXRhdGUocHJvcCA9IG5SZWFkcy90b3QucmVhZHMpICU+JSAKICBncm91cF9ieShIYXNoLCBzb3VyY2UpICU+JSAKICBzdW1tYXJpc2UgKG1heC4gID0gbWF4KHByb3ApLAogICAgICAgICAgICAgbWVhbi4gPSBtZWFuKHByb3ApLAogICAgICAgICAgICAgdG90LiAgPSBzdW0oblJlYWRzKSkgJT4lIAogIGdhdGhlcihjb250YWlucygiLiIpLCB2YWx1ZSA9ICJudW1iZXIiLCBrZXkgPSAiU3RhdCIpICU+JQogIHNwcmVhZChrZXkgPSAic291cmNlIiwgdmFsdWUgPSAibnVtYmVyIiwgZmlsbCA9IDApICU+JSAKICBncm91cF9ieShIYXNoLCBTdGF0KSAlPiUKICBtdXRhdGUob3JpZ2luID0gY2FzZV93aGVuKFBvc2l0aXZlcyA+IFNhbXBsZXMgfiAiUG9zaXRpdmUuY29udHJvbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFICAgICAgICAgICAgICAgIH4gIkVudmlyb25tZW50IikpICU+JSAKICBncm91cF9ieSAoSGFzaCkgJT4lCiAgbXV0YXRlKHRvdCA9IG5fZGlzdGluY3Qob3JpZ2luKSkgLT4gSGFzaC5mYXRlLnN0ZXAyCgpIYXNoLmZhdGUuc3RlcDIgJT4lIAogIGZpbHRlcih0b3QgPT0gMSkgJT4lIAogIGdyb3VwX2J5KEhhc2gpICU+JSAKICBzdW1tYXJpc2Uob3JpZ2luID0gdW5pcXVlKG9yaWdpbikpICU+JSAKICBmaWx0ZXIob3JpZ2luID09ICJQb3NpdGl2ZS5jb250cm9sIikgLT4gSGFzaGVzLnRvLnJlbW92ZS5zdGVwMgoKQVNWLnRhYmxlICU+JSAKICBncm91cF9ieShzb3VyY2UsIEhhc2gpICU+JSAKICBzdW1tYXJpc2Uob2N1cnJlbmNlcyA9bigpKSAlPiUgCiAgc3ByZWFkKGtleSA9IHNvdXJjZSwgdmFsdWUgPSBvY3VycmVuY2VzLCBmaWxsID0gMCkgJT4lIAogICNsZWZ0X2pvaW4oSGFzaGVzLnRvLnJlbW92ZS5zdGVwMikgJT4lIAogICNtdXRhdGUob3JpZ2luID0gY2FzZV93aGVuKGlzLm5hKG9yaWdpbikgfiAiS2VwdCIsCiAgICMgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgICAgICAgICB+ICJEaXNjYXJkZWQiKSkgJT4lIAogIG11dGF0ZShzZWNvbmQub3JpZ2luID0gY2FzZV93aGVuKFBvc2l0aXZlcyA+PSBTYW1wbGVzIH4gIkRpc2NhcmRlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgICAgICAgICAgICAgICAgfiAiS2VwdCIpKSAlPiUgCiAgZmlsdGVyKHNlY29uZC5vcmlnaW4gPT0gIkRpc2NhcmRlZCIpICU+JSAKICBmdWxsX2pvaW4oSGFzaGVzLnRvLnJlbW92ZS5zdGVwMikgLT4gSGFzaGVzLnRvLnJlbW92ZS5zdGVwMgoKSGFzaGVzLnRvLnJlbW92ZS5zdGVwMiAlPiUgCiAgYmluZF9yb3dzKHRpYmJsZShIYXNoID0gYygiMDEyMDRkNTg3YTNjMzc1MmY0MjZmMmY2ZDgxM2MwZmYyYjgwZWM4YiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWNlYmNkNWM0OTFiYjI3M2YzZTRkNjE1Y2FmYWQ2NDkiKSkpIC0+IEhhc2hlcy50by5yZW1vdmUuc3RlcDIKIAoKYGBgCklOIG9yZGVyIHRvIHRyYWluIERBREEyIHRvIGJldHRlciBkaXN0aW5ndWlzaCB3aGVuIHBvc2l0aXZlIGNvbnRyb2wgc2VxdWVuY2VzIGhhdmUgYXJyaXZlZCBpbiB0aGUgZW52aXJvbm1lbnQsIHdlIHdpbGwga2VlcCB0aGUgc2VxdWVuY2VzIGluIGEgY3N2IGZpbGUKCgpgYGB7ciBBU1ZzIGZyb20gcG9zaXRpdmVzfQoKSGFzaGVzLnRvLnJlbW92ZS5zdGVwMiAlPiUgCiAgbGVmdF9qb2luKEhhc2gua2V5KSAlPiUgCiAgc2VsZWN0KEhhc2gsIFNlcXVlbmNlKSAlPiUgCiAgd3JpdGVfY3N2KCIuLi9PdXRwdXQvSGFzaGVzLnRvLnJlbW92ZS5jc3YiKQoKYGBgCgojIyMgUmVtb3ZlIHRoZSBwb3NpdGl2ZSBjb250cm9sIGhhc2hlcyBmcm9tIHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgQVNWcwoKYGBge3IgY2xlYW5pbmcuU3RlcDJ9CgpBU1YubmVzdGVkICU+JSAKICBtdXRhdGUoU3RlcDIudGliYmxlID0gbWFwIChTdGVwLjEubG93LnJlYWRzLCB+IGZpbHRlciguLCFIYXNoICVpbiUgSGFzaGVzLnRvLnJlbW92ZS5zdGVwMiRIYXNoKSAlPiUgdW5ncm91cCkpIC0+IEFTVi5uZXN0ZWQKCnNhdmVSRFMoQVNWLm5lc3RlZCwgZmlsZSA9ICIuLi9JbnB1dC9DbGVhbmluZy5iZWZvcmUuT2NjLm1vZGVsIikKCkFTVi5uZXN0ZWQgPC0gcmVhZFJEUyhmaWxlID0iLi4vSW5wdXQvQ2xlYW5pbmcuYmVmb3JlLk9jYy5tb2RlbCIpCgpBU1YubmVzdGVkICU+JSAKICB0cmFuc211dGUoTWlzZXFfcnVuLCBTdW1tYXJ5LjEgPSBtYXAoU3RlcDIudGliYmxlLCB+IGhvdy5tYW55KEFTVnRhYmxlID0gLixyb3VuZCA9ICIzLlBvc2l0aXZlcyIpKSkgJT4lIAogIGxlZnRfam9pbihBU1Yuc3VtbWFyeSkgJT4lIAogIG11dGF0ZShTdW1tYXJ5ICAgPSBtYXAyKFN1bW1hcnksIFN1bW1hcnkuMSwgYmluZF9yb3dzKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtU3VtbWFyeS4xKSAtPiBBU1Yuc3VtbWFyeSAKCkFTVi5zdW1tYXJ5ICU+JSAKICB1bm5lc3QoKQpgYGAKCiMjIENsZWFuaW5nIFByb2Nlc3MgNDogKipPY2N1cGFuY3kgbW9kZWxsaW5nKioKCldoYXQgaXMgdGhlIHByb2JhYmlsdHkgb2YgYSB0cnVlIHBvc2l0aXZlIHByZXNlbmNlIG9mIGEgSGFzaCBpbiBhIE1pc2VxIFJ1bi4gV2Ugd2lsbCB1c2UgZUROQSBvY2N1cGFuY3kgbW9kZWxpbmcgdG8gYXNzZXMgd2hldGhlciBhIGhhc2ggaXMgYSByYXJlIHZhcmlhbnQgdGhhdCBzcGlsbGVkIG91dCBvciBhIHRydWUgcHJlc2VuY2UuCgpUaGUgcHJvY2VzcyByZXF1aXJlcyB0byBsb2FkIGV4dHJhIHBhY2thZ2VzLCBjcmVhdGUgc29tZSBtb2RlbCBmaWxlLCBhbmQgZ3JvdXAgdGhlIGhhc2hlcyBieSBSdW4sIGFuZCBiaW9sb2dpY2FsIHJlcGxpY2F0ZSwgc3VtbWFyaXNpbmcgdGhlIGRhdGEgaW4gYSBwcmVzZW5jZSBhYnNlbmNlIGZvcm1hdC4KClRoZSBvY2N1cGFuY3kgbW9kZWwgaXRzZWxmIHdhcyBwZXJmb3JtZWQgaW4gdGhlIFJtYXJrZG93biBmaWxlIGBSamFncy50dW5uaW5nLlJtZGAsIHNvIGhlcmUgd2Ugd2lsbCB1cGxvYWQgdGhlIGNzdiBmaWxlIHRoYXQgY29udGFpbnMgYWxsIHByb2JhYmlsaXR5IG9mIG9jY3VyZW5jZXMgb2YgYWxsIGhhc2hlcyBwZXIgc2l0ZS4gRWFjaCBIYXNoLVNpdGUgY29tYmluYXRpb24gcHJvZHVjZXMgYSBtYXRyaXggb2YgcHJlc2VuY2UgYWJhc2NlbmNlcyB0aGF0IGZlZWRzIHRoZSBtb2RlbCAtIGZvciBzb21lIGNhc2VzIGl0IGlzIGEgMzB4MyBtYXRyaXgsIGZvciBvdGhlcnMgaXQgaXMgYSAzOXgzLiBXZSBzdW1tYXJpc2VkIHRoZSBudW1iZXIgb2Ygb2NjdXJlbmNlcyBpbiBlYWNoIGNhc2UgYW5kIHJ1biBtb2RlbHMgZm9yIGVhY2ggdW5pcXVlIGNhc2UgKHRvIHNhdmUgY29tcHV0aW5nIHRpbWUpLiBFYWNoIHVuaXF1ZSBtb2RlbCB3YXMgcnVuIDEwIHRpbWVzIHRvIGZpbHRlciBvdXQgY2FzZXMgaW4gd2hpY2ggdGhlIG1vZGVsIGNvbnZlcmdlIGludG8gYSBsb2NhbCBtYXhpbWEuCgpTbyB3ZSB3aWxsIGltcG9ydCB0aGUgb2JqZWN0IGBPY2MuZmF0ZS5jc3ZgIGFuZCByZWR1Y2UgdGhlIGRhdGFzZXQgdG8gdGhvc2UgSGFzaGVzIHdpdGggYW4gb2NjID4gMC44CgpgYGB7ciBpbXBvcnRpbmcgT2NjIHJlc3VsdHN9CgpvY2MucmVzdWx0cyA8LSByZWFkX2NzdigiLi4vSW5wdXQvT2NjLmZhdGUuY3N2IikKCm9jYy5yZXN1bHRzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSByZWFsKSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiYmx1ZSIpICsKICBsYWJzKHggPSAiIiwKICAgICAgIHkgPSAiTnVtYmVyIG9mIEhhc2hlcyIpICAtPiBsZWZ0LnBsb3QKICAKCm9jYy5yZXN1bHRzICU+JSAKICBsZWZ0X2pvaW4oQVNWLm5lc3RlZCAlPiUgCiAgICAgICAgICAgICAgc2VsZWN0KFN0ZXAyLnRpYmJsZSkgJT4lIAogICAgICAgICAgICAgIHVubmVzdChjb2xzID0gU3RlcDIudGliYmxlKSAlPiUgCiAgICAgICAgICAgICAgZ3JvdXBfYnkoSGFzaCkgJT4lIAogICAgICAgICAgICAgIHN1bW1hcmlzZSAodG90ID0gc3VtKG5SZWFkcykpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gY3V0X2ludGVydmFsKHJlYWwsIG4gPSAyMCkpKSArCiAgZ2VvbV9jb2woYWVzKHkgPSB0b3QpLCBmaWxsID0gInJlZCIpICsKICBsYWJzKHggPSAiIiwKICAgICAgIHkgPSAiTnVtYmVyIG9mIFJlYWRzIikgICAtPiByaWdodC5wbG90CgoKbGVmdC5wbG90ICsgcmlnaHQucGxvdCAKCmBgYAoKU28gd2Ugd2lsbCB0aHJvdyBhd2F5IG1vc3Qgb2YgdGhlIEhhc2hlcywgYnV0IHdpbGwga2VlcCBtb3N0IG9mIHRoZSByZWFkcyAtIHdlIGFyZSBnZXR0aW5nIGludG8gc29tZXRoaW5nIGhlcmUKCmBgYHtyIGFjdHVhbCBmaWx0ZXJpbmd9CiBvY2MucmVzdWx0cyAlPiUgCiAgZmlsdGVyKHJlYWwgPiAwLjgpICU+JSAKICBwdWxsIChIYXNoKSAtPiB0by5rZWVwCgpBU1YubmVzdGVkICU+JSAKICBtdXRhdGUoU3RlcDMudGliYmxlID0gbWFwIChTdGVwMi50aWJibGUsIH4gZmlsdGVyKC4sSGFzaCAlaW4lIHRvLmtlZXApKSkgLT4gQVNWLm5lc3RlZAoKQVNWLm5lc3RlZCAlPiUgCiAgdHJhbnNtdXRlKE1pc2VxX3J1biwgU3VtbWFyeS4xID0gbWFwKFN0ZXAzLnRpYmJsZSwgfiBob3cubWFueShBU1Z0YWJsZSA9IC4scm91bmQgPSAiNC5PY2N1cGFuY3kiKSkpICU+JSAKICBsZWZ0X2pvaW4oQVNWLnN1bW1hcnkpICU+JSAKICBtdXRhdGUoU3VtbWFyeSAgID0gbWFwMihTdW1tYXJ5LCBTdW1tYXJ5LjEsIGJpbmRfcm93cykpICU+JQogIGRwbHlyOjpzZWxlY3QoLVN1bW1hcnkuMSkgLT4gQVNWLnN1bW1hcnkgCgoKYGBgCgoKIyMgQ2xlYW5pbmcgUHJvY2VzcyA1OiAqKkRpc3NpbWlsYXJpdHkgYmV0d2VlbiBQQ1IgcmVwbGljYXRlcyoqCgpTbywgYSBzZWNvbmQgd2F5IG9mIGNsZWFuaW5nIHRoZSBkYXRhc2V0IGlzIHRvIHJlbW92ZSBzYW1wbGVzIGZvciB3aGljaCB0aGUgZGlzc2ltaWxhcml0eSBiZXR3ZWVuIFBDUiByZXBsaWNhdGVzIGV4Y2VlZHMgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgZGlzc2ltaWxhcml0aWVzLgpTb21ldGltZXMgdGhlIHByZXBhcmF0aW9uIG9mIGEgUENSIHJlcGxpY2F0ZSBnb2VzIHdyb25nIGZvciBhIG51bWJlciBvZiByZWFzb25zIC0gdGhhdCBsZWFkcyB0byBhIHBhcnRpY3VsYXIgUENSIHJlcGxpY2F0ZSB0byBiZSBzdWJzdGFudGlhbGx5IGRpZmZlcmVudCB0byB0aGUgb3RoZXIgMi4gSW4gdGhhdCBjYXNlLCB3ZSB3aWxsIHJlbW92ZSB0aGUgUENSIHJlcGxpY2F0ZSB0aGF0IGhhcyBoaWdoZXIgZGlzc2ltaWxhcml0eSB3aXRoIHRoZSBvdGhlciB0d28uCgpUaGUgcHJvY2VzcyBzdGFydHMgYnkgYWRkaW5nIHRoZSBiaW9sb2dpY2FsIGluZm9ybWF0aW9uIHRvIHRoZSBBU1YgdGFibGUsIHRoZW4gZGl2aW5nIHRoZSBkYXRhc2V0IGJ5IHRoZWlyIGJpb2xvZ2ljYWwgcmVwbGljYXRlLiBUaGlzIHdpbGwgYWxzbyByZW1vdmUgYW55IHNhbXBsZSB0aGF0IGlzIG5vdCBpbmNsdWRlZCBpbiB0aGUgbWV0YWRhdGEsIGVnIGNvbWluZyBmcm9tIGEgZGlmZmVyZW50IHByb2plY3QuCgpgYGB7ciBkaXNzaW1pbGFyaXR5IGJldHdlZW4gUENSIHJlcGxpY2F0ZXN9CgpBU1YubmVzdGVkICU+JSAKICBzZWxlY3QoTWlzZXFfcnVuLCBTdGVwMy50aWJibGUpICU+JSAKICB1bm5lc3QoU3RlcDMudGliYmxlKSAlPiUKICBzZXBhcmF0ZShzYW1wbGUsIGludG8gPSAib3JpZ2luYWxfc2FtcGxlIiwgc2VwID0gIlxcLiIsIHJlbW92ZSA9IEYpIC0+IGNsZWFuZWQudGliYmxlCmBgYAoKCmBgYHtyIHF1aWNrIGNoZWNrfQojIGRvIGFsbCBzYW1wbGVzIGhhdmUgYSBuYW1lCmNsZWFuZWQudGliYmxlICU+JSAKICBmaWx0ZXIgKHNhbXBsZSA9PSAiIikKIyBkbyBhbGwgb2YgdGhlbSBoYXZlIGFuIG9yaWdpbmFsIHNhbXBsZQpjbGVhbmVkLnRpYmJsZSAlPiUgCiAgZmlsdGVyKG9yaWdpbmFsX3NhbXBsZSA9PSAiIikKIyBkbyBhbGwgb2YgdGhlbSBoYXZlIGEgSGFzaApjbGVhbmVkLnRpYmJsZSAlPiUgCiAgZmlsdGVyKGlzLm5hKEhhc2gpKQojIEhvdyBtYW55IHNhbXBsZXMsIGhvdyBtYW55IEhhc2hlcwpjbGVhbmVkLnRpYmJsZSAlPiUgCiAgc3VtbWFyaXNlKG5fZGlzdGluY3Qoc2FtcGxlKSwgIyA3NzAKICAgICAgICAgICAgbl9kaXN0aW5jdChIYXNoKSkgICAjIDQ1NzEKCiMgTGV0J3MgY2hlY2sgdGhlIGxldmVscyBvZiByZXBsaWNhdGlvbgoKY2xlYW5lZC50aWJibGUgJT4lIAogIGdyb3VwX2J5KG9yaWdpbmFsX3NhbXBsZSkgJT4lIAogIHN1bW1hcmlzZShucmVwID0gbl9kaXN0aW5jdChzYW1wbGUpKSAlPiUgCiAgI2ZpbHRlciAobnJlcCA9PSAyKSAjIDEzCiAgZmlsdGVyIChucmVwID09IDEpICMgMCAKYGBgCk9rLCBzbyB0aGVyZSBhcmUgMTMgc2FtcGxlcyBmb3Igd2hpY2ggd2Ugb25seSBoYXZlIDIgUENSIHJlcGxpY2F0ZXMxLiAgIFdlIHdpbGwgZ2V0IHJpZCBvZiB0aG9zZSB3aXRoIG9ubHkgMSwgYXMgd2UgY2FuJ3QgZXN0aW1hdGUgdGhlIFBDUiBiaWFzIHRoZXJlLgoKYGBge3IgcmVtb3ZlIHNpbmdsZSByZXBsaWNhdGVzfQpkaXNjYXJkLjEgPC0gY2xlYW5lZC50aWJibGUgJT4lIAogIGdyb3VwX2J5KG9yaWdpbmFsX3NhbXBsZSkgJT4lIAogIG11dGF0ZShucmVwID0gbl9kaXN0aW5jdChzYW1wbGUpKSAlPiUgCiAgI2ZpbHRlciAobnJlcCA9PSAyKSAjIDI1CiAgZmlsdGVyIChucmVwID09IDEpICU+JSAKICBkaXN0aW5jdChzYW1wbGUpICU+JSBwdWxsKHNhbXBsZSkKCmNsZWFuZWQudGliYmxlICU+JSAKICBmaWx0ZXIoIXNhbXBsZSAlaW4lIGRpc2NhcmQuMSkgLT4gY2xlYW5lZC50aWJibGUKYGBgCgpBbnl3YXksIGxldCdzIGhhdmUgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRpc3NpbWlsYXJpdGllcyBiZXR3ZWVuIFBDUiByZXBsaWNhdGVzLCBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgYW5kIGV2ZXJ5dGhpbmcgZWxzZS4KCmBgYHtyIGxldHMgZG8gdGhlIFBDUiByZXBsaWNhdGlvbn0KY2xlYW5lZC50aWJibGUgJT4lCiAgZ3JvdXBfYnkgKHNhbXBsZSkgJT4lCiAgbXV0YXRlIChUb3QgPSBzdW0oblJlYWRzKSwKICAgICAgICAgIFJvdy5zdW1zID0gblJlYWRzIC8gVG90KSAlPiUgCiAgZ3JvdXBfYnkgKEhhc2gpICU+JQogIG11dGF0ZSAoQ29sbWF4ID0gbWF4IChSb3cuc3VtcyksCiAgICAgICAgICBOb3JtYWxpemVkLnJlYWRzID0gUm93LnN1bXMgLyBDb2xtYXgpIC0+IGNsZWFuZWQudGliYmxlCnRpYmJsZV90b19tYXRyaXggPC0gZnVuY3Rpb24gKHRiKSB7CiAgCiAgdGIgJT4lIAogICAgZ3JvdXBfYnkoc2FtcGxlLCBIYXNoKSAlPiUgCiAgICBzdW1tYXJpc2UoblJlYWRzID0gc3VtKE5vcm1hbGl6ZWQucmVhZHMpKSAlPiUgCiAgICBzcHJlYWQgKCBrZXkgPSAiSGFzaCIsIHZhbHVlID0gIm5SZWFkcyIsIGZpbGwgPSAwKSAtPiBtYXRyaXhfMQogICAgc2FtcGxlcyA8LSBwdWxsIChtYXRyaXhfMSwgc2FtcGxlKQogICAgbWF0cml4XzEgJT4lIAogICAgICB1bmdyb3VwKCkgJT4lIAogICAgZHBseXI6OnNlbGVjdCAoIC0gc2FtcGxlKSAtPiBtYXRyaXhfMQogICAgZGF0YS5tYXRyaXgobWF0cml4XzEpIC0+IG1hdHJpeF8xCiAgICBkaW1uYW1lcyhtYXRyaXhfMSlbWzFdXSA8LSBzYW1wbGVzCiAgICB2ZWdkaXN0KG1hdHJpeF8xKSAtPiBtYXRyaXhfMQp9Cgp0aWJibGVfdG9fbWF0cml4IChjbGVhbmVkLnRpYmJsZSkgLT4gYWxsLmRpc3RhbmNlcy5mdWxsCgojbmFtZXMoYWxsLmRpc3RhbmNlcy5mdWxsKQoKCnN1bW1hcnkoaXMubmEobmFtZXMoYWxsLmRpc3RhbmNlcy5mdWxsKSkpCmBgYAoKTGV0J3MgbWFrZSB0aGUgcGFpcndhaXNlIGRpc3RhbmNlcyBhIGxvbmcgdGFibGUKYGBge3J9Cgphcy50aWJibGUoc3Vic2V0KG1lbHQoYXMubWF0cml4KGFsbC5kaXN0YW5jZXMuZnVsbCkpKSkgLT4gYWxsLmRpc3RhbmNlcy5tZWx0ZWQKCnN1bW1hcnkoaXMubmEoYWxsLmRpc3RhbmNlcy5tZWx0ZWQkdmFsdWUpKQoKIyBOb3csIGNyZWF0ZSBhIHRocmVlIHZhcmlhYmxlcyBmb3IgYWxsIGRpc3RhbmNlcywgdGhleSBjb3VsZCBiZSBQQ1IgcmVwbGljYXRlcywgQklPTCByZXBsaWNhdGVzLCBvciBmcm9tIHRoZSBzYW1lIHNpdGUKCmFsbC5kaXN0YW5jZXMubWVsdGVkICU+JQogIHNlcGFyYXRlIChWYXIxLCBpbnRvID0gIkJvdHRsZTEiLCBzZXAgPSAiXFwuIiwgcmVtb3ZlID0gRkFMU0UpICU+JQogIHNlcGFyYXRlIChCb3R0bGUxLCBpbnRvID0gIlNpdGUxIiwgcmVtb3ZlID0gRkFMU0UpICU+JQogIHNlcGFyYXRlIChWYXIyLCBpbnRvID0iQm90dGxlMiIsIHNlcCA9ICJcXC4iLCByZW1vdmUgPSBGQUxTRSkgJT4lCiAgc2VwYXJhdGUgKEJvdHRsZTIsIGludG8gPSAiU2l0ZTIiLCByZW1vdmUgPSBGQUxTRSkgJT4lCiAgbXV0YXRlICggRGF5LnNpdGUxID0gc3RyX3N1YihCb3R0bGUxLCBzdGFydCA9IDEsIGVuZCA9IC0yKSwKICAgICAgICAgICBEYXkuc2l0ZTIgPSBzdHJfc3ViKEJvdHRsZTIsIHN0YXJ0ID0gMSwgZW5kID0gLTIpLAogICAgICAgICAgIERpc3RhbmNlLnR5cGUgPSBjYXNlX3doZW4oIEJvdHRsZTEgPT0gQm90dGxlMiB+ICJQQ1IucmVwbGljYXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF5LnNpdGUxID09IERheS5zaXRlMiB+ICJCaW9sLnJlcGxpY2F0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNpdGUxID09IFNpdGUyIH4gIlNhbWUgU2l0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJEaWZmZXJlbnQgU2l0ZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICU+JQogIGRwbHlyOjpzZWxlY3QoU2FtcGxlMSA9IFZhcjEsIFNhbXBsZTIgPSBWYXIyICwgdmFsdWUgLCBEaXN0YW5jZS50eXBlKSAlPiUKICBmaWx0ZXIgKFNhbXBsZTEgIT0gU2FtcGxlMikgLT4gYWxsLmRpc3RhbmNlcy50by5wbG90CgojIENoZWNraW5nIGFsbCB3ZW50IHdlbGwKCnNhcHBseShhbGwuZGlzdGFuY2VzLnRvLnBsb3QsIGZ1bmN0aW9uKHgpIHN1bW1hcnkoaXMubmEoeCkpKQoKYWxsLmRpc3RhbmNlcy50by5wbG90JERpc3RhbmNlLnR5cGUgPC0gYWxsLmRpc3RhbmNlcy50by5wbG90JERpc3RhbmNlLnR5cGUgICU+JSBmY3RfcmVsZXZlbCggIlBDUi5yZXBsaWNhdGVzIiwgIkJpb2wucmVwbGljYXRlcyIsICJTYW1lIFNpdGUiKQoKICBnZ3Bsb3QgKGFsbC5kaXN0YW5jZXMudG8ucGxvdCAsIGFlcyAoZmlsbCA9IERpc3RhbmNlLnR5cGUsIHggPSB2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbSAocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gJ2RlbnNpdHknLCBhbHBoYSA9IDAuOSkgKwogIyBmYWNldF93cmFwKCB+IERpc3RhbmNlLnR5cGUpICsKICBsYWJzICh4ID0gIlBhaXJ3aXNlIGRpc3NpbWlsYXJpdHkiLCB5ID0gImRlbnNpdHkiICwKICAgICAgICBEaXN0YW5jZS50eXBlID0gIkRpc3RhbmNlIikKICAKYGBgCgoKCmBgYHtyfQojIEluc3RlYWQgb2YgY2hvc2luZyBiYXNlZCBvbiB0aGUgcHcgZGlzdGFuY2VzLCB3ZSBjYW4gZG8gYSBzaW1pbGFyIHRoaW5nIHVzaW5nIHRoZSBkaXN0YW5jZSB0byBjZW50cm9pZAoKIyBGaW5kIG91dCB3aGljaCBzYW1wbGVzIGhhdmUgb25seSB0d28gcGNyIHJlcGxpY2F0ZXMKY2xlYW5lZC50aWJibGUgJT4lIGRwbHlyOjpzZWxlY3QoLU1pc2VxX3J1bikgJT4lIGdyb3VwX2J5KG9yaWdpbmFsX3NhbXBsZSkgJT4lIG5lc3QoKSAtPiBuZXN0ZWQuY2xlYW5pbmcKCm5lc3RlZC5jbGVhbmluZyAlPiUgCiAgbXV0YXRlKG1hdHJpeCA9IG1hcChkYXRhLCB0aWJibGVfdG9fbWF0cml4KSkgLT4gbmVzdGVkLmNsZWFuaW5nCm5lc3RlZC5jbGVhbmluZyAlPiUgbXV0YXRlKG5jb21wYXJpc29ucyA9IG1hcChtYXRyaXgsIGxlbmd0aCkpIC0+IG5lc3RlZC5jbGVhbmluZwogCiAgCmRpc3RfdG9fY2VudHJvaWQgPC0gZnVuY3Rpb24gKHgseSkgewogIGJpb2wgPC0gcmVwKHksIGxlbmd0aCh4KSkKICAKICBpZiAobGVuZ3RoKGJpb2wpID09IDEpIHsKICAgIG91dHB1dCA9IHJlcCh4WzFdLzIsMikKICAgIG5hbWVzKG91dHB1dCkgPC0gYXR0cih4LCAiTGFiZWxzIikKICB9ZWxzZXsgCiAgICAKICBkaXNwZXJzaW9uIDwtIGJldGFkaXNwZXIoeCwgZ3JvdXAgPSBiaW9sKQogIG91dHB1dCA9IGRpc3BlcnNpb24kZGlzdGFuY2VzCiAgfQogIG91dHB1dAogICAgfQoKCm5lc3RlZC5jbGVhbmluZyA8LSBuZXN0ZWQuY2xlYW5pbmcgJT4lIG11dGF0ZSAoZGlzdGFuY2VzID0gbWFwMihtYXRyaXgsIG9yaWdpbmFsX3NhbXBsZSwgZGlzdF90b19jZW50cm9pZCkpCgp1bmxpc3QgKG5lc3RlZC5jbGVhbmluZyRkaXN0YW5jZXMpIC0+IGFsbF9kaXN0YW5jZXMKCgpgYGAKCmBgYHtyfQojbm9ybXBhcmFtcyA8LSBmaXRkaXN0cihhbGxfcGFpcndpc2VfZGlzdGFuY2VzLCAibm9ybWFsIikkZXN0aW1hdGUKbm9ybXBhcmFtcyA8LSBNQVNTOjpmaXRkaXN0cihhbGxfZGlzdGFuY2VzLCAibm9ybWFsIikkZXN0aW1hdGUKIyAgcHJvYnMgPC0gcG5vcm0oYWxsX3BhaXJ3aXNlX2Rpc3RhbmNlcywgbm9ybXBhcmFtc1sxXSwgbm9ybXBhcmFtc1syXSkKcHJvYnMgPC0gcG5vcm0oYWxsX2Rpc3RhbmNlcywgbm9ybXBhcmFtc1sxXSwgbm9ybXBhcmFtc1syXSkKb3V0bGllcnMgPC0gd2hpY2gocHJvYnM+MC45NSkKCmRpc2NhcmQgPC1uYW1lcyAoYWxsX2Rpc3RhbmNlc1tvdXRsaWVyc10pCgoKdG9fd3JpdGVfZGlzY2FyZGVkIDwtIGFzLnRpYmJsZShhbGxfZGlzdGFuY2VzW291dGxpZXJzXSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIGRwbHlyOjpzZWxlY3Qoc2FtcGxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZV90b19jZW50cm9pZCA9IHZhbHVlKQp0b193cml0ZV9kaXNjYXJkZWQgPC0gdG9fd3JpdGVfZGlzY2FyZGVkICU+JSBiaW5kX3Jvd3ModGliYmxlKHNhbXBsZSA9IGRpc2NhcmQuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZV90b19jZW50cm9pZCA9IE5BKSkKd3JpdGVfY3N2KHRvX3dyaXRlX2Rpc2NhcmRlZCAsIi4uL091dHB1dC9kaXNjYXJlZF9zYW1wbGVzLmNzdiIpCgojIFdobyBwYXNzZXMgdGhpcyBmaWx0ZXIKCmFsbF9kaXN0YW5jZXMgJT4lCiAgYXMudGliYmxlKCkgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBuYW1lcyhhbGxfZGlzdGFuY2VzKSkgJT4lIAogIGZpbHRlcighc2FtcGxlICVpbiUgdG9fd3JpdGVfZGlzY2FyZGVkJHNhbXBsZSkgJT4lIAogIHNlcGFyYXRlKHNhbXBsZSwgaW50byA9ICJldmVudCIsIHNlcCA9IC0zKSAlPiUgCiAgZ3JvdXBfYnkoZXZlbnQpICU+JSAKICBzdW1tYXJpc2UoY2FzZXMgPSBuKCkpICU+JSAKICBzZXBhcmF0ZShldmVudCwgaW50byA9IGMoIlNpdGUiLCAiRGF0ZSIpLCByZW1vdmUgPSBGKSAlPiUgCiAgZmlsdGVyKERhdGUgIT0gIjIwMTcwMyIpICU+JSAKICBnZ3Bsb3QoKSsKICBnZW9tX3Jhc3RlcihhZXMoeD0gRGF0ZSwgeSA9IFNpdGUsIGZpbGwgPSBjYXNlcykpKwogIGdlb21fdGV4dChhZXMoeD0gRGF0ZSwgeSA9IFNpdGUsIGxhYmVsID0gY2FzZXMpKQogIAogIAogIApgYGAKCkZpbmFsbHksIGxldCdzIHJlbW92ZSB0aGVzZSBzYW1wbGVzIGZyb20gdGhlIGRhdGFzZXQKCmBgYHtyIGFjdHVhbCBjbGVhbmluZ30KCkFTVi5uZXN0ZWQgJT4lIAogIG11dGF0ZShTdGVwNC50aWJibGUgPSBtYXAgKFN0ZXAzLnRpYmJsZSwgIH4gZmlsdGVyKC4sISBzYW1wbGUgJWluJSB0b193cml0ZV9kaXNjYXJkZWQkc2FtcGxlKSkpIC0+IEFTVi5uZXN0ZWQKCkFTVi5uZXN0ZWQgJT4lIAogIHRyYW5zbXV0ZShNaXNlcV9ydW4sIFN1bW1hcnkuMSA9IG1hcChTdGVwNC50aWJibGUsIH4gaG93Lm1hbnkoQVNWdGFibGUgPSAuLHJvdW5kID0gIjUuUENSLmRpc3NpbWlsYXJpdHkiKSkpICU+JSAKICBsZWZ0X2pvaW4oQVNWLnN1bW1hcnkpICU+JSAKICBtdXRhdGUoU3VtbWFyeSAgID0gbWFwMihTdW1tYXJ5LCBTdW1tYXJ5LjEsIGJpbmRfcm93cykpICU+JQogIGRwbHlyOjpzZWxlY3QoLVN1bW1hcnkuMSkgLT4gQVNWLnN1bW1hcnkgCmBgYAoKCiMjIEV4cG9ydGluZyB0aGUgb3V0cHV0CgpXZSB3aWxsIGV4cG9ydCB0aGUgZmluYWwgY2xlYW5lZCB0YWJsZSB3aXRoIGZvdXIgY29sdW1ucyAoTWlzZXFfcnVuLCBzYW1wbGUsIEhhc2gsIG5SZWFkcykKCmBgYHtyfQoKQVNWLm5lc3RlZCAlPiUgCiAgc2VsZWN0KE1pc2VxX3J1biwgU3RlcDQudGliYmxlKSAlPiUgCiAgdW5uZXN0KFN0ZXA0LnRpYmJsZSkgJT4lIAogIG11dGF0ZShuUmVhZHMgPSBhcy5pbnRlZ2VyKG5SZWFkcykpICU+JSAKICB3cml0ZV9jc3YoIi4uL091dHB1dC9BU1ZfdGFibGVfYWxsX3RvZ2V0aGVyLmNzdiIpCgpBU1YubmVzdGVkICU+JSAKICBzZWxlY3QoU3RlcDQudGliYmxlKSAlPiUgCiAgdW5uZXN0KFN0ZXA0LnRpYmJsZSkgJT4lIAogIGRpc3RpbmN0KEhhc2gpICU+JSAKICBsZWZ0X2pvaW4oSGFzaC5rZXkpICU+JSAKICB3cml0ZV9jc3YoIi4uL091dHB1dC9IYXNoX0tleV9hbGxfdG9nZXRoZXIuY3N2IikKCgoKaW5wdXQgPC0gcmVhZF9jc3YoIi4uL091dHB1dC9IYXNoX0tleV9hbGxfdG9nZXRoZXIuY3N2IikKb3V0cHV0IDwtICIuLi9PdXRwdXQvSGFzaF9LZXlfYWxsX3RvZ2V0aGVyLmNzdiIKCndyaXRlLmZhc3RhIChzZXF1ZW5jZXMgPSBhcy5saXN0KGlucHV0JFNlcXVlbmNlKSwKICAgICAgICAgICAgIG5hbWVzID0gYXMubGlzdChpbnB1dCRIYXNoKSwKICAgICAgICAgICAgIGZpbGUub3V0ID0gb3V0cHV0KQoKCmBgYAoKIyMgU3VtbWFyeSBvZiB0aGUgY2xlYW51cCBwcm9jZXNzCgpgYGB7ciBsYXN0IGdyYXBofQoKQVNWLnN1bW1hcnkgJT4lIAogIHVubmVzdCgpICU+JSAKICBnZ3Bsb3QoYWVzKHg9U3RhZ2UsIHk9bnVtYmVyLCBmaWxsID0gU3RhdCkpKwogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IE1pc2VxX3J1biwgY29sb3IgPSBNaXNlcV9ydW4pKSsKICBmYWNldF9ncmlkKFN0YXR+Liwgc2NhbGVzID0gImZyZWUiKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9MSkpIywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCgoKYGBgCgoK